Senior Production Week 4 - The Gift Shop
This week, I was in charge of building the new Gift Shop level. I was fairly excited for this, because it was the first time I would be working on something new, for this project and I had the entire puzzle to myself since the other programmers, were either working on the Control room, or gameplay systems. It also made more sense, for the Gift Shop to be a one person job, because most of the interactions all used 1 or 2 scripts.
As you can see from the picture, I didn't have time to implement the artwork, but I should be able to do that within the next 24-48 hours. For now I guess everyone will have to deal with my placeholder art.
The main systems I had to write were:
Item Spawner + Item Spawning
Remote controller + Remote controlled object
Removing uncaught items
Cart carrying items + Checkout
The plane ($6 football shaped thing on the right) is controlled by first selecting it, then using the left stick to angle it up and down, and right trigger to "pull back the rubber band" to then launch the plane. This was pretty simple. I just wrote a small script, detecting if it is selected, and if it is, to set its angle relative to the y axis of the left stick. Then I scale the angle based on a min and max angle set in the inspector, so the designers can have more control over the possible launch angles. I then set the launch speed to be equal to the right trigger's value scaled by a maxLaunchVelocity variable in the inspector.
As an extra thing, I added a custom attribute [ReadOnly] that programmers can now place in front of serialized variables so they show in the inspector, but greyed out. This is because sometimes, I want to see the information for debugging but I want to make it clear, this is not for editing. I also like to arrange my variables under headers for "References", "Settings", and readonly "Data". The reason I have 3 values at the top, not under any header is because those are from a script that airplane inherits and therefor, can't be moved manually. I guess next is a custom attribute for automatically arranging variables based on category like in Unreal. Assuming that's possible.
Next was a plane collision script which has been applied to the plant ($56 cylinder in the center) and the whale ($7 dollar ball to the right of the plant). What this does, is when the plane collides with the object, it applies a custom force (set in the inspector) so that we can have more control over how the object reacts to the plane's collision. It is still pretty janky, but works most of the time. I definitely will need to play around with it to make it more consistent.
There wasn't enough variables to warrant using the categories, so instead I used them to explain the variables. Mostly because I hate tooltips, since they are easily missed, and I need these to be noticable.
As you can see, I also wrote a small bit of code, to reset the object when it gets stuck... which happens a lot. When they are knocked off, if they land on the counter, they are supposed to slide back and off the counter, out of view, then reset. But sometimes they don't and get stuck. This is just to help stop that from happening. I do this by calling Invoke("ResetObject", StuckProtectTimer); which will call ResetObject after 1 second. If reset is called before that 1 second, it cancels the Invoke so as to not reset it twice.
Item Spawner + Item Spawning
Next was item spawning. For Item spawning I wrote a base script for spawnable items which PlaneCollisionInteraction, and plane extend. (that's where those mystery variable came from).
It contains a reference to it's rigidbody and the dispenser that manages it. It also has a monetary value and a public field that controls whether it can reset or note (not shown).
The item dispenser, uses an object pool, where all the items exist from the start, but are inactive. It then, to spawn a new item, picks from the inactive items, and move it to the active portion, then activates the item. It contains a setting for the remove height which just repools the item if below objectRemoveHeight. timeForNewObject, is the user set time to spawn a new object, once the current object has been moved out of the way. A dispenser object has a function called ResetObject which just checks "canReset", if it is true, calls it's dispenser's RemoveObject function passing itself as a parameter. I don't like this solution because I don't think the Item should know about the manager. But it works for now.
Remote Controller + Remote Controlled Object
The last thing was a remote controlled cart. For this, the designers wanted 2 buttons on the bottom (white rectangles on the bottom center), that when selected would move the cart, ( |_| shaped thing above the buttons). To do this, I wrote a remote controller script and a remote controlled object script.
The remote controller lets you hook up functions to each event. Select, deselect, select + stick-left, select + stick-right, etc. It then invokes the appropriate events during interactions. I hooked the functions up to a remote controlled object script which looked like this:
It has a variable for speed, and bounds to keep it in. In this case, it just goes right to left, so the up/down and forward/backward are the same. I also use Rigidbody.move() instead of manually setting the position, because it is supposed to carry objects, and this allows for it to place nicely with physics. It also automatically will help prevent it from clipping through things. It has 6 functions for moving it in the 6 directions. As you can see from the controll(er) script, I hooked up right and left (shown left) to the buttons.
When the cart goes all the way to the left, it is supposed to "knock" the shelf, which causes the the hand ontop to tip over the edge. All I did for this was write a script to add to the shelf, which gets triggered when the cart touches it. It then applies a force the same way the plane does, to the hand.
In the image you can see a slanted shelf in between the objects and the cart below. This prevents the cart from being able to catch any falling items. To solve this, the player must knock over the plant which when it hits the shelf, the shelf will wobble. If they knock a second plant onto the shelf, the shelf will break and fall. This was pretty easy. I just made a quick little animation of it wobbling, then a second where it falls (using physics but helped with the animator to make sure it doesn't get stuck). It stays in it's idle state, until the plant touches it. This causes the trigger "Hit" to be sent to the shelf's animator, which causes it to progress to the wobble state. The state only plays once, but it will stay in that state until it gets the "Hit" trigger again, in which case, it plays the falling animation. Now it is out of the way for items to fall onto the counter below or into the cart.
Removing uncaught items
If an item is not caught by the cart, it needs to slide off behind the counter. All I did for this was, create a script called counter, which applies a force in the Z direction to any items touching it. I then added it to the counter and any surface the airplane could get stuck on. This meant applying it to the platforms the items spawn on. So I also had to disable their physics until their respective triggers get called whether that was getting hit by the plane, or the cart hitting their shelf.
Cart carrying items + Checkout
The cart has an invisible barrier on the z sides, that stop items from falling out. It also stops the counter's z push force from being applied to any items inside that happen to clip through and touch the counter. When the cart moves all the way to the right, where the cash register is (big cube on the right with $0), if the contents' total value = $55, the player wins the puzzle, and it dispenses a pair of sunglasses. If the cost is anything else, the cart tips in forward in the z direction dumping all the items out.
That is pretty much how this week went. I pretty much had everything implemented in some form, by the end of the first day. I then spent the rest of my time working on trying to get the physics to behave with the static animations and scripted movements. Next all I have to do is implement the artwork and the room can be considered... almost finished... I still have to iron out some bugs that appear every once and a while. Like the plane glitching out and spinning in place, or items sometimes just not respawning. One thing at a time.