This starts off after we add the jump code. Please start from the end of Week 6-B's activity. This activity uses Unity and all code should allow you to copy and paste.
For information on how to create a new Unity project and what each of the windows/tabs mean, please see Week 5-D Step 1.
This code is for a MarioMove.cs script within the Assets folder in Unity. If you create it by scratch, you will need to add this script to your Mario game object and assign your sprites and Mario game object within your Mario Move component (inside the Inspector Tab).
Individual values might need to be updated to fit your tastes. It should appear something like this:
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMarioMove:MonoBehaviour{publicGameObject mario;publicSprite stand;publicSprite walk1;publicSprite walk2;publicSprite walk3;publicSprite jump;publicfloat speed =3;publicfloat delay;publicfloat delayReset =1;publicint walkingImage =1;publicbool isWalking =false;publicint direction =0; // 0 = right 1 = leftpublicbool isJumping =false;publicfloat jumpHeight =500; // Start is called before the first frame updatevoidStart() { delay = delayReset; } // Update is called once per framevoidUpdate() { delay -=1*Time.deltaTime;if (delay <=0) {if (isWalking ==true&& isJumping !=true) {Walk(); } delay = delayReset; }if (isJumping ==true) {mario.GetComponent<SpriteRenderer>().sprite= jump; }if (Input.GetKey(KeyCode.RightArrow) ==true) {mario.transform.Translate(Vector3.right* speed *Time.deltaTime); isWalking =true; direction =0;FaceDirection(); }if (Input.GetKey(KeyCode.LeftArrow) ==true) {mario.transform.Translate(Vector3.left* speed *Time.deltaTime); isWalking =true; direction =1;FaceDirection(); }if (Input.GetKey(KeyCode.RightArrow) !=true&&Input.GetKey(KeyCode.LeftArrow) !=true&& isJumping !=true) { // Sets the sprite for mario to standmario.GetComponent<SpriteRenderer>().sprite= stand; isWalking =false; }if (Input.GetKeyDown(KeyCode.Space) ==true) {if (isJumping !=true) {mario.GetComponent<Rigidbody2D>().AddForce(Vector2.up* jumpHeight); isJumping =true; } } }publicvoidWalk() {if (walkingImage ==1) { // Sets the sprite for mario to walk1mario.GetComponent<SpriteRenderer>().sprite= walk1; walkingImage =2; delay = delayReset; }elseif (walkingImage ==2) { // Sets the sprite for mario to walk2mario.GetComponent<SpriteRenderer>().sprite= walk2; walkingImage =3; delay = delayReset; }else { // Sets the sprite for mario to walk3mario.GetComponent<SpriteRenderer>().sprite= walk3; walkingImage =1; delay = delayReset; } }publicvoidFaceDirection() { // if the x scale is negative, it's facing leftif (mario.transform.localScale.x<0) {if (direction ==0) // if Mario should be facing right, update it { // set a new scale using the current values and multiplying x by -1 to turn it positive mario.transform.localScale = new Vector3(mario.transform.localScale.x * -1, mario.transform.localScale.y, mario.transform.localScale.z);
} } // if the x scale is positive, it's facing rightif (mario.transform.localScale.x>0) {if (direction ==1) // if Mario should be facing left, update it { // set a new scale using the current values and multiplying x by -1 to turn it negative mario.transform.localScale = new Vector3(mario.transform.localScale.x * -1, mario.transform.localScale.y, mario.transform.localScale.z);
} } }publicvoidOnCollisionEnter2D(Collision2D collision) {if (collision.gameObject.name=="Ground") { isJumping =false; } }}
One more thing to add before starting
Click on your Mario object to bring up its components in the Inspector window.
Under Rigidbody 2D, twirl down Constraints and check Freeze Rotation > Z. Otherwise, Mario will fall over from the force.
Step 1: Import Your Coin Sprite
Feel free to use this image or import in another image to serve as your "coin."
Click and drag it from your computer's folder into the Assets folder within your Project Window in Unity.
You'll notice it has a white background because it's just an image right now.
Let's change it to a sprite.
Click on the coin image. In the Inspector Window, change the Texture Type to Sprite (2D and UI).
Click the Apply button.
This should turn the image into a sprite with a transparent background.
Step 2: Create a Coin Prefab
2a. Add example coin to scene
To create a prefab (a reusable object), we need to make an example of that object.
Drag the coin sprite into your scene and resize it to your liking.
2b. Create the spinning coin code
The coins will use their own code to move and communicate to a centralized object.
In the Project window, go to the Assets folder. In the window, right-click and create a new folder called Scripts and go to it.
Create a new script by right-clicking and choosing Create > C# Script
It will appear like this:
Name it something like CoinBehavior.
Click and drag it or use the Add Component button in the Inspector Window to add the CoinBehavior script component to the coin object in the scene.
It should appear like this in the inspector.
Open it in your text editor (preferably Visual Studio).
Here, we want the coin to spin.
Since the code will only be used on the coin, we can access the transform component simply by using "transform." The script will look for a transform component within the object it is attached to.
In Update(), add the following code:
transform.Rotate(Vector3.up);
Rotate() is a built-in function inside the transform class - the set of code that controls and stores information for transform components.
Vector3.up is shorthand for new Vector3(0, 1, 0), so the coin will spin on the Y axis.
Modify this with Time.deltaTime to normalize the speed.
transform.Rotate(Vector3.up*Time.deltaTime);
To adjust this speed, add a variable at the top for speed and multiply it to the Rotate() argument.
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCoinBehavior:MonoBehaviour{publicfloat spinSpeed =100; // Start is called before the first frame updatevoidStart() { } // Update is called once per framevoidUpdate() {transform.Rotate(Vector3.up*Time.deltaTime* spinSpeed); }}
Save and test!
2c. Have the coin fall
We want the coins to fall at the same rate, so we should store the fall speed in a separate, centralized object with a script.
I generally call this centralized object and script GameController. You can name it what you want as long as it matches the references in the code.
Right-click in the Hierarchy window and choose Create Empty.
Rename the new, empty object:
In the Project window, right-click and create a new C# Script.
Name it. REMEMBER: no spaces and capitalize it!
Add the script to the Game Controller object.
Open the new script.
Add a public variable at the top (above Start()) to store the fall speed:
publicfloat fallSpeed =1;
Save the file.
Go to or open the CoinBehavior script.
Add a variable to access the Game Controller and a variable to hold the fall speed info.
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCoinBehavior:MonoBehaviour{publicfloat spinSpeed =100;publicGameObject gameController;publicfloat coinFallSpeed; // Start is called before the first frame updatevoidStart() { coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; } // Update is called once per framevoidUpdate() { // Set fall speed coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; // Spin on Y axistransform.Rotate(Vector3.up*Time.deltaTime* spinSpeed); // Fall downwardtransform.position+=Vector3.down*Time.deltaTime* coinFallSpeed; }}
Go back to Unity.
Click on the coin object in the Hierarchy or Scene window to bring up its components in the Inspector window.
Use the target icon or click and drag the Game Controller object to the field for Game Controller.
Press play to test!
2d. Create the Prefab
Create a Prefab folder in your Assets folder within the Project folder. (This is an optional step that helps with organization.)
Click and drag your coin object from the Hierarchy window to the Project window into your new Prefabs folder.
It will look like this in the Project window:
And it will appear blue within the Hierarchy window:
To test, click and drag the prefab from the Project window to the Scene window.
Click on one of them - you'll see that Game Controller is not assigned. That is because this prefab is available for other scenes where the Game Controller in this scene doesn't exist.
So let's have the coin find and assign the available Game Controller to itself:
Open the CoinBehavior script.
We know the Game Controller in the scene will be the only object with the Game Controller script. We can get the Game Controller object by finding the script first.
In the Start() function, set the gameController variable with this script:
FindObjectOfType<>() is a built-in function that looks for the first object with the component listed in the angle brackets <>. After we get that, we can get the proper part of the object (here, it is the GameObject) we need.
Save and return to Unity. Hit play to test.
The Game Controller field should update and the coins should fall at the same rate. You can change the Fall Speed in Game Controller to see if all coins are affected.
Step 3: Spawning Coins
Let's use this coin prefab to "spawn" or "instantiate" at a random location within the left and right edges of the frame, but just outside the top of the frame.
Unity has a built-in function called Instantiate() that takes the arguments for the object to spawn and a Vector 3 for where to spawn it.
Open the GameController script.
Since multiple coins will spawn, we will have the Game Controller do the work.
Since we have a delay, we want to have it countdown and trigger the spawn when it hits zero and then reset.
It loops, so put it in Update().
voidUpdate(){ // Counts down spawnDelay by 1 * Time.deltatime spawnDelay -=Time.deltaTime; // When spawnDelay hits zero, run code and restartif (spawnDelay <=0) { // Insert spawn code here // Reset spawnDelay spawnDelay = spawnDelayReset; }}
Let's use Instantiate() and our coin prefab to spawn the coins.
// Spawns coin prefabInstantiate(coinPrefab);
This spawns the coin prefab at the place our example one was.
To be safe, let's update the position of our prefab.
Save your script and return to Unity.
Remove all but one of your coins.
Click on the arrow to the right of the name of the coin object in your Hierarchy window.
This opens the coin prefab in your Scene window. Any updates in this mode will affect all your instances of that prefab object.
While coin is selected, right-click on the Transform component in the Inspector window and choose "Reset Position."
This might move the coin out of the Scene window. Click on the coin object in Hierarchy and press the letter F to focus on it.
Click the left arrow to the left of the name of the prefab to return to the scene or double-click on the scene in the Project window.
Select the Game Controller object in the Hierarchy window to bring up its components in the Inspector window.
Click and drag your coin prefab from the Project window to the field for Coin Prefab in the Game Controller script.
Test it by playing it!
The coins should spawn at the center of the scene.
Now, let's add the extra positioning.
Click and drag your coin object in the Scene window, looking at the Transform component noting the Y coordinate when it's just out of frame in the Game window. Also, note the farthest left and right X coordinates you want.
For mine, it's 8 for the Y axis and and -8.5 (farthest left) and 8.5 (farthest right).
Open the GameController script.
Add another argument for the location for the coin to spawn. For the x-axis, I'm using the built-in random function to pick a number between -8.5 and 8.5 and setting the Y-axis to 8. The Z-axis is 0 because we are working in 2 dimensions.
This version of Instantiate() also needs a rotation, so we'll just use the rotation the prefab already has.
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassGameController:MonoBehaviour{publicfloat fallSpeed =1;publicGameObject coinPrefab;publicfloat spawnDelay;publicfloat spawnDelayReset =2; // Start is called before the first frame updatevoidStart() { spawnDelay = spawnDelayReset; } // Update is called once per framevoidUpdate() { // Counts down spawnDelay by 1 * Time.deltatime spawnDelay -=Time.deltaTime; // When spawnDelay hits zero, run code and restartif (spawnDelay <=0) { // Spawns coin prefabInstantiate(coinPrefab,newVector3(Random.Range(-8.5F,8.5F),8,0),coinPrefab.transform.rotation); // Reset spawnDelay spawnDelay = spawnDelayReset; } }}
Save your script and return to Unity.
Remove the final coin object from your Hierarchy window.
Press play to test.
The coins should spawn randomly outside of the frame and fall slowly down.
Step 4: Collisions
Now, we want to have points and speed increase when Mario hits a coin.
We need to first give our coin prefab a collider.
Click on your coin prefab in the Project window to bring up its components in the Inspector window.
Click the Add Component button and use the search bar to find Box Collider 2D component. Select it to add it.
Since we are looking at what the coin is hitting, we need to also add a Rigidbody2D component the same way we did the collider.
We don't want gravity, so set Gravity Scale to zero.
Now, open the CoinBehavior script.
At the bottom before the final ending curly bracket } let's add the built-in function that collects information about the item the object hits.
We only want the coins to spawn when the game is running so wrap the Instantiate() code in an if statement:
if (gameOver !=true){ // Spawns coin prefabInstantiate(coinPrefab,newVector3(Random.Range(-8.5F,8.5F),8,0),coinPrefab.transform.rotation);}
Put in a situation where the game ends.
if (fails >=3){ gameOver =true;}
Full code of GameController so far:
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassGameController:MonoBehaviour{publicfloat fallSpeed =1;publicGameObject coinPrefab;publicfloat spawnDelay;publicfloat spawnDelayReset =2;publicbool gameOver =false;publicint score =0;publicint fails =0; // Start is called before the first frame updatevoidStart() { spawnDelay = spawnDelayReset; } // Update is called once per framevoidUpdate() { // Counts down spawnDelay by 1 * Time.deltatime spawnDelay -=Time.deltaTime; // When spawnDelay hits zero, run code and restartif (spawnDelay <=0) {if (gameOver !=true) { // Spawns coin prefabInstantiate(coinPrefab,newVector3(Random.Range(-8.5F,8.5F),8,0),coinPrefab.transform.rotation); } // Reset spawnDelay spawnDelay = spawnDelayReset; }if (fails >=3) { gameOver =true; } }}
Go back to your CoinBehavior script.
Let's increase the score and speed when a coin hits Mario and increase the fails and speed when the coin hits the ground.
publicvoidOnCollisionEnter2D(Collision2D collision){if (collision.gameObject.name=="Mario") { // Gets the score in the Game Controller script and adds 1gameController.GetComponent<GameController>().score+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }if (collision.gameObject.name=="Ground") { // Gets the fails in the Game Controller script and adds 1gameController.GetComponent<GameController>().fails+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }}
collision serves as an object of "Collision2D" type that stores information about the object being hit. If the name of the object matches the name of the Mario or Ground object, we can run code.
Once the coin hits anything, let's have it destroy itself with the built-in function Destroy() at the end of the OnCollisionEnter2D() function.
Destroy(gameObject);
CoinBehavior script so far:
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCoinBehavior:MonoBehaviour{publicfloat spinSpeed =100;publicGameObject gameController;publicfloat coinFallSpeed; // Start is called before the first frame updatevoidStart() { gameController =FindObjectOfType<GameController>().gameObject; coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; } // Update is called once per framevoidUpdate() { // Set fall speed coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; // Spin on Y axistransform.Rotate(Vector3.up*Time.deltaTime* spinSpeed); // Fall downwardtransform.position+=Vector3.down*Time.deltaTime* coinFallSpeed; }publicvoidOnCollisionEnter2D(Collision2D collision) {if (collision.gameObject.name=="Mario") { // Gets the score in the Game Controller script and adds 1gameController.GetComponent<GameController>().score+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }if (collision.gameObject.name=="Ground") { // Gets the fails in the Game Controller script and adds 1gameController.GetComponent<GameController>().fails+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }Destroy(gameObject); }}
Save and test your game in Unity!
We don't have an interface for the score in our game, so for now, you can see the score and fails by selecting the Game Controller object and watching the values change as you play.
Final Codes
GameController.cs script
Attached to Game Controller object
Set the Coin Prefab variable to the coin prefab from the Project window.
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassGameController:MonoBehaviour{publicfloat fallSpeed =1;publicGameObject coinPrefab;publicfloat spawnDelay;publicfloat spawnDelayReset =2;publicbool gameOver =false;publicint score =0;publicint fails =0; // Start is called before the first frame updatevoidStart() { spawnDelay = spawnDelayReset; } // Update is called once per framevoidUpdate() { // Counts down spawnDelay by 1 * Time.deltatime spawnDelay -=Time.deltaTime; // When spawnDelay hits zero, run code and restartif (spawnDelay <=0) {if (gameOver !=true) { // Spawns coin prefabInstantiate(coinPrefab,newVector3(Random.Range(-8.5F,8.5F),8,0),coinPrefab.transform.rotation); } // Reset spawnDelay spawnDelay = spawnDelayReset; }if (fails >=3) { gameOver =true; } }}
MarioMove.cs script
Attached to Mario object
Add the Mario game object from the Hierarchy window as the variable mario.
Add stand, walk1, walk2, walk3, and jump sprites from the Project window as the sprite variables.
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassMarioMove:MonoBehaviour{publicGameObject mario;publicSprite stand;publicSprite walk1;publicSprite walk2;publicSprite walk3;publicSprite jump;publicfloat speed =3;publicfloat jumpHeight =300;publicfloat delay;publicfloat delayReset =1;publicint walkingImage =1;publicbool isWalking =false;publicbool isJumping =false;publicint direction =0; // 0 = right 1 = left // Start is called before the first frame updatevoidStart() { delay = delayReset; } // Update is called once per framevoidUpdate() { delay -=1*Time.deltaTime;if (delay <=0) {if (isWalking ==true) {Walk(); } delay = delayReset; }if (isJumping ==true) {mario.GetComponent<SpriteRenderer>().sprite= jump; }if (Input.GetKey(KeyCode.RightArrow) ==true) {mario.transform.Translate(Vector3.right* speed *Time.deltaTime); isWalking =true; direction =0;FaceDirection(); }if (Input.GetKey(KeyCode.LeftArrow) ==true) {mario.transform.Translate(Vector3.left* speed *Time.deltaTime); isWalking =true; direction =1;FaceDirection(); }if (Input.GetKeyDown(KeyCode.Space) ==true) {if (isJumping !=true) {mario.GetComponent<Rigidbody2D>().AddForce(Vector2.up* jumpHeight); isJumping =true; } }if (Input.GetKey(KeyCode.RightArrow) !=true&&Input.GetKey(KeyCode.LeftArrow) !=true&& isJumping !=true) { // Sets the sprite for mario to standmario.GetComponent<SpriteRenderer>().sprite= stand; isWalking =false; } }publicvoidWalk() {if (walkingImage ==1) { // Sets the sprite for mario to walk1mario.GetComponent<SpriteRenderer>().sprite= walk1; walkingImage =2; delay = delayReset; }elseif (walkingImage ==2) { // Sets the sprite for mario to walk2mario.GetComponent<SpriteRenderer>().sprite= walk2; walkingImage =3; delay = delayReset; }else { // Sets the sprite for mario to walk3mario.GetComponent<SpriteRenderer>().sprite= walk3; walkingImage =1; delay = delayReset; } }publicvoidFaceDirection() { // if the x scale is negative, it's facing leftif (mario.transform.localScale.x<0) {if (direction ==0) // if Mario should be facing right, update it { // set a new scale using the current values and multiplying x by -1 to turn it positive mario.transform.localScale = new Vector3(mario.transform.localScale.x * -1, mario.transform.localScale.y, mario.transform.localScale.z);
} } // if the x scale is positive, it's facing rightif (mario.transform.localScale.x>0) {if (direction ==1) // if Mario should be facing left, update it { // set a new scale using the current values and multiplying x by -1 to turn it negative mario.transform.localScale = new Vector3(mario.transform.localScale.x * -1, mario.transform.localScale.y, mario.transform.localScale.z);
} } }publicvoidOnCollisionEnter2D(Collision2D collision) {if (collision.gameObject.name=="Ground") { isJumping =false; } }}
CoinBehavior.cs script
Attached to the coin prefab (within the Project window)
usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCoinBehavior:MonoBehaviour{publicfloat spinSpeed =100;publicGameObject gameController;publicfloat coinFallSpeed; // Start is called before the first frame updatevoidStart() { gameController =FindObjectOfType<GameController>().gameObject; coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; } // Update is called once per framevoidUpdate() { // Set fall speed coinFallSpeed =gameController.GetComponent<GameController>().fallSpeed; // Spin on Y axistransform.Rotate(Vector3.up*Time.deltaTime* spinSpeed); // Fall downwardtransform.position+=Vector3.down*Time.deltaTime* coinFallSpeed; }publicvoidOnCollisionEnter2D(Collision2D collision) {if (collision.gameObject.name=="Mario") { // Gets the score in the Game Controller script and adds 1gameController.GetComponent<GameController>().score+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }if (collision.gameObject.name=="Ground") { // Gets the fails in the Game Controller script and adds 1gameController.GetComponent<GameController>().fails+=1; // Gets the fallSpeed in the Game Controller and adds 1gameController.GetComponent<GameController>().fallSpeed+=1; }Destroy(gameObject); }}