Stack Ball 3D - Made with Unity
A remake of a hyper casual game with Unity.
Game Features:
Stack Ball is a 3d arcade game where players smash, bump and bounce through revolving helix platforms to reach the end.
- Simple and easy to pick-up
- Great time killer
Your ball smashes like a brick through colorful platforms that block its descent, but if you hit a black one, it's all over! Your ball shatters to pieces and you have to start your fall all over again.
But even black platforms are no match for a fireball falling at full speed! Choose your strategy: speed up like a madman or stop and wait for your next chance to roll and jump. Other ball games wish they were this fun!
Tech details:
Rotate stacks
Rotator is the parent of all stacks, check out the code and comment to understand Rotator.cs below:
Note that since all of the stacks are children of Rotator, so when Rotator rotates, all stacks rotate.
// Rotator.cs
// Rotator.cs is attached to the empty gameobject at 0,0,0 not the green cylinder.
public class Rotator : MonoBehaviour
{
public float speed = 100;
void Update()
{
transform.Rotate(new Vector3(0, speed * Time.deltaTime, 0));
// all of the stacks are children of Rotator, so it rotates will cause all stacks rotate
}
}
Adjust the center cylinder’s position and rotation to make it look nicer in the game scene.
How to realize that each stack is offset by a certain rotation from top to bottom?
for loop: {
tempStack = Instantiate(prefab)
tempStack .transform.position = new Vector3(0, i-0.01f, 0); // every stack is offset 0.01f from the last one.
tempStack .transform.eulerAngles = new Vector3(0, i*8,0)
}
The ending section prefab will be instantiate and added to the scene after all stacks are added.
We will base on the last stack’s Y position to set the endingSection gameobject’s Y position.
Stack
Black part of the stack:
Only one part is black, when the ball is smashing down and touches it, the game fails.
Black part is tagged with “plane”
Green parts are tagged with “enemy”
if (target.gameObject.tag == "enemy")
{
target.transform.parent.GetComponent<StackController>().ShatterAllParts();
}
if (target.gameObject.tag == "plane")
{
rb.isKinematic = true;
transform.GetChild(0).gameObject.SetActive(false);
playerState = PlayerState.Died; // game fails
SoundManager.instance.PlaySoundFX(deadClip, 0.5f);
}
Note that when the ball collides with black part, the ball’s rigidbody’s isKinematic is set to true.
If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore. The rigidbody will be under full control of animation or script control by changing transform.position.
Basically, the ball won’t bounce back anymore.
Stack shatters
Each part is attached with script:StackPartController.cs
this script will control what will happen to each part when shattering:
1) rigidBody.isKinematic = false;// will be affected by gravity after it is shattered.
2) collider.enabled = false; // disable the collider
3) rigidBody.AddForceAtPosition(dir * force, forcePoint, ForceMode.Impulse);
4) rigidBody.AddTorque(Vector3.left * torque);
About 1)
Initially, every part’s rigidbody’s isKinematic is set to true, useGravity is set to true.
So that every part won’t fall down until the ball shatters them. When shattering, just set each part’s isKinematic to false.
About 3)
the dir is calculated by:
Vector3 dir = (Vector3.up * 1.5f + subDir).normalized;
First, the up direction is the basic direction, the shattered part will fly up.
Then the subDir is left or right based on the shattered part’s position relative to the center of the stack.
Game Control
User input handling:
Detect if is pressing the screen, if so then meaning the ball should be smashing down.
if (Input.GetMouseButtonDown(0))
smash = true;
if (Input.GetMouseButtonUp(0))
smash = false;
In regards to detecting input, don’t put code in FixedUpdate, you may risk missing the user input event.
Next we will implement the ball smashing down logic.
In FixedUpdate(), when smash is true, we set the rigidbody’s velocity on the negative y axis to make it go down at a speed.
rb.velocity = new Vector3(0, -100 * Time.fixedDeltaTime * 7, 0);
Invincible mode charging
currentTime represents the progress of charging to invincible mode.
currentTime will be the variable that increments and decrements between 0 and 1.
if (smash) // if smashing, reward the player with increment of currentTime which represents the progress of charging to invincible mode
currentTime += Time.deltaTime * .8f;
else // if not smashing, the progress will go down, but 0.5f is slower thant 0.8f
currentTime -= Time.deltaTime * .5f;
Invincible mode counting down
if (invincible)
{
currentTime -= Time.deltaTime * .35f; // counting down 35% every second.
if (!fireEffect.activeInHierarchy)
fireEffect.SetActive(true);
}
if (currentTime <= 0)
{
currentTime = 0;
invincible = false; // when currentTime decreased to 0, invincible ends.
invincibleFill.color = Color.white;
}
Invincible progress UI
The type of the image is Filled which make the effect of progress bar.
Polish
Always has visual effects even idle is vital to hyper casual games.
How to make the Splash Effect when the ball hit the stack.
In Player.cs
GameObject splash = Instantiate(splashEffect);
splash.transform.SetParent(target.transform); // stays on the stack later on
we set the stack which the ball hits as the splash’s parent, then later on, the splash will stay where it is and rotate with the stack.
splash.transform.position = new Vector3(transform.position.x, transform.position.y - 0.22f, transform.position.z);
here transform.position is the global position of the ball.
Leave a comment
Log in with itch.io to leave a comment.