Goals in the Pinball Games iOS and tvOS Starter Kit
Every CartoonSmart Starter Kit has a feature that we think really raises the bar, in terms of what you can do with the kit. In the Story Teller’s Kit 2, this was a featured named Event Listeners, which allowed you to listen for all kinds of variables basically, and respond to them, In the Platform Games Kit, just being able to design worlds almost entirely within Xcode (and with very few properties) then play them with external game controllers, checked off some bucket-list goal from when I was a 10 year old playing the original NES. Goals in this kit are really what sets it apart from just being a template to move around art and have stuff collide on the table. Goals are how you are going to craft a pinball game that players are addicted to.
Take for example, a ramp. By itself, it’s a fun device to send the ball up through. The ball sails over other objects it would normally collide with and land elsewhere on the table. But when the player realizes they have to send the ball up that same ramp, 8 or 10 times, suddenly it becomes a mission. And if that mission has to be restarted when the ball is lost, players become committed to keeping the ball in play. This is the crux of any good pinball table. People need something to work toward to keep them invested.
You can see in the image below, our Old West Table has plenty of Goals, some with obscure names, some that might give you an idea of their purpose just by reading the name (like ExtraBall). Point is, you get to design these goals yourself, and the kit supports an unlimited number of them. So don’t think you are constrained by our suggestions, or existing names.
In the green box in the screenshot above, you can see the Goals used to light up the letters E-X-T-R-A B-A-L-L (and ultimately award an extra ball). One letter is lit each time the player’s ball goes over the ramp. Not seen in the image, there is a Light, named ExtraBallTrigger around the upper curve of the ramp, which increments each goal every time the ball contacts it.
In the left image below, you can see the AddsToGoal property for the hidden ExtraBallTrigger on the ramp. On the right, two of the Goals are unfolded: LightUpE and LightUpX. Notice the AchievedAt values are 1 and 2 (circled in green).
Every letter spelling E-X-T-R-A B-A-L-L has a higher AchievedAt value than the previous letter. When the final goal, LightUpL2 (the second L) has been achieved, an extra ball is awarded using the AddBall property (circle below). Notice the AchievedAt value is 9, so at that point, the ball would have rolled over the ExtraBallTrigger 9 times.
Obviously the goal has other properties, all of which we will discuss in this article.
What Can Add To Goals?
Setting the AddsToGoal property in any of your TableObject or Light dictionaries is normally how you will increment goals. The kit also has a few other places to add to goals. In the TableSettings dictionary, you can add the properties…
- BallLostGoals – will add to this goal every time the ball is lost down the drain. This might just be used to activate animations or play sounds after a ball loss.
- HighScoreGoals – will add to this goal once per game if a new high score is achieved. The app saves the highest all time score as a NSUserDefault, so even if the app is closed, the highest score ever is not lost.
You can also add to a goal every time the Plunger is used. In this case, the AddsToGoal property is set for a specific plunger….
Keep in mind, since the plunger can be used multiple times in a row (if the ball doesn’t escape the plunger alley), this could potentially run many times per round. So most likely, you will only want to use this goal for minor events, like playing sounds or animations.
- AchievedAt – a number value to note when this goal is achieved. If the number is 1, the goal would immediately be achieved when something adds to this goal. Goals are only achieved once (unless they are reset when achieved). So for example, adding to the same goal again, after it has been achieved, will not trigger the achievements again.
Swift coders only from this point on… NSNotifications and NSUserDefaults are the backbone of the goal system. A notification is sent to the GameScene (normally from a Light or TableObject) with the name of the goal to increment up. In the screenshot below you can see the function that receives the posted notification. The notification parameter contains the “goalName” as part of the userInfo. So for example, a goal named “ExtraBall” might be pulled from this userInfo…
We then create an integer variable named currentGoalAmount that is equal to what the NSUserDefaults has saved for a key matching that goal name, in this example, that key name would be “ExtraBall”
The defaults then sets the same key to a new integer value of currentGoalAmount + 1.
And finally a much longer function named addToGoalWithName is called to check the Goals dictionary for that goal name and see if its AchievedAt value is equal to what the defaults has saved for it.
Point is, a capable coder could easily create their own goal properties and check for them in the addToGoalWithName function or any other function that checks the value of defaults.integerForKey ( “[whatever the goal name is]” )Also covered in detail in the Pinball Games tutorials
Resetting or Adding / Subtracting Other Goals
- ResetGoals – after a goal has been achieved, you might want to reset that goal (or any number of other goals). This property can be a String or Array of Strings listing each goal to reset (including itself). For example…
- AddsToGoal – a String or Array of Strings listing other goals to add to when the current goal is achieved.
- SubtractsFromGoal – – a String or Array of Strings listing other goals to subtract 1 from when the current goal is achieved
Goals Related to the Ball or Other Nodes
- MakeDynamic – a String or Array of Strings. Each value is the name of an object to become dynamic (part of the physics simulation). The object will also be affected by gravity. If the objects you list here are of the Ball class, and those balls are not already dynamic, this will activate multi ball mode in the game (zooming out may occur depending on your zoom settings for the table). This property is mostly intended to start multi-ball mode, but really any non-dynamic object could be made dynamic.
- MoveBallToNode – a String value matching the name of any node you want to move the ball to. This could be an Empty Node (simple placeholder)
- MoveNodeToNode – a Dictionary value listing keys and values of an object to move (the key) and where to move it (the value). In the example image below, the SafetyBumper is immediately moved to the position of the SafetyPlaceholder (an Empty Node).
- AnimateNodeToNode – a Dictionary value, which functions identical to the previous property, but the object moves to the new position over two seconds. You could hard-code in specific time intervals in the GameScene_Operations.swift file if need be. Just search for this property name.
- QuickAnimateNodeToNode – a Dictionary value, which functions identical to the previous property, but the object moves to the new position over one seconds. You could hard-code in specific time intervals in the GameScene_Operations.swift file if need be. Just search for this property name.
- Remove – a String or Array of Strings with values matching the names of nodes to completely remove from the table. Careful, these won’t come back until the table is reloaded.
- Hide – a String or Array of Strings with values matching the names of nodes to hide on the table. These can be set to show again via a goal or if their ResetOnBallLoss property is set to YES.
- Show – a String or Array of Strings with values matching the names of nodes to show on the table.
- Reset – a String or Array of Strings with values matching the names of either TableObjects or Lights to reset. Resetting them will return them to their original look or position. It will also subtract any goals the object has contributed to.
- PlayAnimation1 through PlayAnimation8 – (these 8 properties function identically) a String or Array of Strings with values matching the names of either TableObjects or Lights to play one of their stored animations.
- LightUp – a String or Array of Strings with values matching the names of Lights to activate. This will turn on their collision animations even if they haven’t been contacted.
Rewards and Feedback
- AddBall (or AddBalls or ExtraBall also works) – A number value for the amount of balls to add to the players remaining ball count.
- Score – A number value for the amount of score to add to the current player’s score.
- Multiplier – A number value for the score multiplier. All new scores will be multiplied by this amount. So a value of 2 would multiply all new points by double. Multipliers are always set back to 1 when the ball is lost.
- AdvanceTable – A String value matching a table dictionary, for example Table2, to advance to. This will reload the entire scene, and go to a brand new table with all scores reset.
- AdvanceTableWithScore – A String value matching a table dictionary, for example Table2, to advance to. This will reload the entire scene, and go to a brand new table with the current scores maintained (for both player 1 and 2)
- Sounds (or Sound ) – this property allows you to play a sound (or random sound) when the goal is achieved. This value can be a String or Array of String values of sound file names (each name should match a sound file imported to the project). Names should include the file extension. If an array is set, a sound is picked at random.
- SoundSequence – like the property above, but if you include an Array of sound file names, they will play in the order listed.
- AchievementText – text to display in the AchievementLabel when the goal is achieved.
- AchievementText2 – text to display in the AchievementLabel2 when the goal is achieved.
You can place all sorts of physics fields into the scene by dragging them from the Object Library onto your table. These can have some WILD effects on the ball or other non-dynamic objects. You can see some of the fields listed below…
To make a field have an effect on the ball or another physics based object, you need to set the CategoryMask of the Field to match the Field Mask of the object. To keep things simple, just use 1. The MainBall and ExtraBalls in the kit already have 1 set.
Any fields you want to be temporarily activated by a goal should have their Strength set to 0, so the field is turned off initially. The goal will then set the Strength to 1, for as many seconds as specified in the value. For example, the ActivateTurbulence goal below uses the property TurnOnTurbulenceFieldFor activate a field named TurbulenceField for 5 seconds….
The properties below all function the same way, by temporarily turning the strength of the field to 1 for a set number of seconds.
- TurnOnTurbulenceFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named TurbulenceField
- TurnOnLinearGravityFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named LinearGravityField
- TurnOnRadialGravityFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named RadialGravityField
- TurnOnSpringFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named SpringField
- TurnOnDragFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named DragField
- TurnOnVortexFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named VortexField
- TurnOnNoiseFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named NoiseField
- TurnOnVelocityFieldFor – A number value for seconds to activate the field. Requires a field node in the scene named VelocityField