Game Design
Class 08: Game BuildTopics
- Menu UI
- Levels
- Platformer (Menu & Levels) Lab Demonstration
- 2D Platformer Project
Class 08 doesn’t look so good!
Menu UI
UI Objects:
There are a variety of UI objects in Unity. They may all attach to the Canvas. Some are purely visual but others may be interacted with, such as buttons, toggle, slider, and input field.
Buttons:
Buttons work the same as those in other UI systems. They “listen” for an event (click) and you may choose how it “handles” it.
Levels
Scenes:
Unity scenes are the equivalent to levels. You create each scene (level) and add them to the build index then use UnityEngine.SceneManagement to load them via code.
Static Objects:
Some elements of your game you will want to carry over to each scene of the game, such as the player, UI, gamec controller, etc. You may do this by using the command, DontDestroyOnLoad().
Platformer (refined mechanics) Lab Demonstration
Assets
- Environment
-
- Foreground
- The imagery that is in front of the player
- Midground
- The imagery that the player character can actually contact, ground, platforms, walls, etc.
- Background
- The imagery behind the player
- Characters
-
- Player
- The protagonist controlled by the player
- Enemies
- The antagonist that kills the player
- Other(pickups, UI, etc.)
-
- Lives
- Adds life to the players total lives
- Points
- Adds to the player’s total score
We now have our graphics complete. You can create more as you go.
Scripts
- New Scripts
-
- None
- No new scripts this week
- Updated Scripts
-
- PlayerControllerScript
- Add raycasting for detection and lerping for jumping
- Old Scripts
-
- PlayerPickupScript
- Detects pickups such as lives or score
- DeathboxScript
- Runs the death function when player enters
- VictoryboxScript
- Loads next level when player enters
- PlayerStatsScript
- Keeps track of the player stats such as lives and score
- EnemyControllerScript
- Moves the enemy sprite and kills the player
- GroundCheckScript
- Checks that the player character is on the ground
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float movSpd = 2.5f;
public float jmpStr = 50f;
public float jmpBst = 1f;
public bool ground = false;
public bool doubleJump = false;
private Rigidbody2D pRB2D;
private Animator pAnim;
private SpriteRenderer pSprite;
private AudioSource pAudio;
private GameObject pJumpParticle;
public AudioClip enemyDeath;
public AudioClip playerJump;
private PlayerStats pStats;
void Awake()
{
pRB2D = GetComponent<Rigidbody2D>();
pAnim = GetComponent<Animator>();
pSprite = GetComponent<SpriteRenderer>();
pAudio = GetComponent<AudioSource>();
pStats = GetComponent<PlayerStats>();
pJumpParticle = transform.GetChild(0).gameObject;
}
void Start()
{
pJumpParticle.transform.SetParent(null);
}
void Update()
{
pRB2D.velocity = new Vector2(Input.GetAxis("Horizontal") * movSpd, pRB2D.velocity.y);
if(pRB2D.velocity.x > 0.1f) pSprite.flipX = false;
if(pRB2D.velocity.x < -0.1f) pSprite.flipX = true;
if(Input.GetButtonDown("Jump"))
{
if(ground)
{
pAudio.clip = playerJump;
pAudio.Play();
pJumpParticle.transform.position = transform.position;
pJumpParticle.GetComponent<ParticleSystem>().Play();
pRB2D.AddForce(Vector2.up * (jmpStr * jmpBst));
ground = false;
doubleJump = true;
}
else
{
if(doubleJump)
{
pAudio.clip = playerJump;
pAudio.Play();
pRB2D.AddForce(Vector2.up * (jmpStr * jmpBst));
doubleJump = false;
}
}
}
pAnim.SetBool("Ground", ground);
pAnim.SetFloat("Speed", Mathf.Abs(Input.GetAxis("Horizontal")));
if(transform.position.y <= -10)
{
pStats.UpdateLives(-1);
}
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.CompareTag("Ground"))
ground = true;
}
void OnCollisionExit2D(Collision2D col)
{
if(col.gameObject.CompareTag("Ground"))
ground = false;
}
public void PowerUp(string type)
{
switch(type)
{
case "Jump Boost":
StartCoroutine(JumpBoost());
break;
}
}
IEnumerator JumpBoost()
{
jmpBst = 2;
yield return new WaitForSeconds(10);
jmpBst = 1;
}
void FixedUpdate()
{
CheckEnemyBelow();
}
void CheckEnemyBelow()
{
RaycastHit2D hit = Physics2D.Raycast(new Vector2(transform.position.x, transform.position.y - .5f), -Vector2.up, .5f);
Debug.DrawRay(new Vector2(transform.position.x, transform.position.y - .5f), -Vector2.up * 0.5f, Color.blue, 2, false);
if(hit.collider != null)
{
if(hit.collider.CompareTag("Enemy"))
{
pAudio.clip = enemyDeath;
pAudio.Play();
Destroy(hit.collider.gameObject);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
public float enemySpeed = 2;
private int enemyDirection = 1;
private Rigidbody2D eRB2D;
private SpriteRenderer eGraphic;
void Awake()
{
eRB2D = GetComponent<Rigidbody2D>();
eGraphic = GetComponentInChildren<SpriteRenderer>();
}
void Update()
{
Move();
}
void Move()
{
eRB2D.velocity = new Vector2 ((enemySpeed * enemyDirection), transform.position.y);
}
void OnTriggerExit2D(Collider2D col)
{
enemyDirection *= -1;
if(eGraphic.flipX) eGraphic.flipX = false;
else eGraphic.flipX = true;
if(eGraphic.flipY) eGraphic.flipY = false;
else eGraphic.flipY = true;
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.CompareTag("Player"))
{
col.gameObject.GetComponent<PlayerStats>().UpdateLives(-1);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeleeAttack : MonoBehaviour
{
private SpriteRenderer pSprite;
public Vector2 attackXpos;
private GameObject enemy;
void Awake()
{
pSprite = GetComponentInParent<SpriteRenderer>();
}
void Start()
{
attackXpos = transform.position;
}
void Update()
{
if(Input.GetButtonDown("Fire1"))
{
if(pSprite.flipX)
{
transform.localPosition = new Vector2(-attackXpos.x, 0);
}
else
{
transform.localPosition = new Vector2(attackXpos.x, 0);
}
if(enemy)
{
Destroy(enemy);
}
}
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.CompareTag("Enemy"))
{
enemy = col.gameObject;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.CompareTag("Enemy"))
{
enemy = null;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PauseMenu : MonoBehaviour
{
public GameObject pauseMenuCanvas;
void Start()
{
pauseMenuCanvas.SetActive(false);
}
void Update()
{
if(Input.GetButtonDown("Cancel"))
{
PauseScreen();
}
}
public void PauseScreen()
{
pauseMenuCanvas.SetActive(!pauseMenuCanvas.activeInHierarchy);
if(Time.timeScale == 1 && pauseMenuCanvas.activeInHierarchy)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
public void LoadStartScreen()
{
Time.timeScale = 1;
SceneManager.LoadScene(0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
public Transform pTransform;
public Vector3 offset;
void Update()
{
transform.position = pTransform.position + offset;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Victory : MonoBehaviour
{
void Awake()
{
DontDestroyOnLoad(GameObject.Find("StaticObjects"));
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.CompareTag("Player") && col.gameObject.GetComponent<PlayerStats>().hasKey)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;
public class PlayerStats : MonoBehaviour
{
public int playerLives = 3;
public int playerScore = 0;
public bool hasKey = false;
private Vector2 startPos;
public TextMeshProUGUI livesText;
public TextMeshProUGUI scoreText;
private GameObject pStartPosition;
public GameObject gameOverScreen;
void OnEnable()
{
//Tell our 'OnLevelFinishedLoading' function to start listening for a scene change as soon as this script is enabled.
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
void OnDisable()
{
//Tell our 'OnLevelFinishedLoading' function to stop listening for a scene change as soon as this script is disabled. Remember to always have an unsubscription for every delegate you subscribe to!
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
pStartPosition = GameObject.Find("PlayerStartPosition");
if(pStartPosition != null)
{
transform.position = pStartPosition.transform.position;
}
}
void Awake()
{
gameOverScreen.SetActive(false);
}
void Start()
{
UpdateLives(0);
UpdateScore(0);
if(pStartPosition != null)
{
transform.position = pStartPosition.gameObject.transform.position;
}
startPos = transform.position;
}
public void UpdateLives(int lives)
{
playerLives += lives;
livesText.text = "Lives: " + playerLives;
if(lives < 0)
transform.position = startPos;
if(playerLives <= 0)
{
gameOverScreen.SetActive(true);
StartCoroutine(EndGame());
}
}
public void UpdateScore(int points)
{
playerScore += points;
scoreText.text = "Score: " + playerScore;
}
IEnumerator EndGame()
{
Time.timeScale = .1f;
yield return new WaitForSeconds(.2f);
SceneManager.LoadScene(0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartMenuScript : MonoBehaviour
{
public GameObject staticObjects;
void Start()
{
staticObjects = GameObject.Find("StaticObjects");
if(staticObjects != null)
{
Destroy(staticObjects);
}
}
public void StartGame()
{
SceneManager.LoadScene("Level01");
}
public void QuitGame()
{
Application.Quit();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pickup : MonoBehaviour
{
private Animator pickupAnim;
public string pickup = "life";
void Awake()
{
pickupAnim = GetComponentInChildren<Animator>();
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.CompareTag("Player"))
{
switch(pickup)
{
case "Life":
col.gameObject.GetComponent<PlayerStats>().UpdateLives(1);
Destroy(gameObject);
break;
case "Point":
col.gameObject.GetComponent<PlayerStats>().UpdateScore(1);
if(pickupAnim)
pickupAnim.SetTrigger("Score");
StartCoroutine(WaitToKill(0.25f));
break;
case "Jump Boost":
col.gameObject.GetComponent<PlayerController>().PowerUp("Jump Boost");
Destroy(gameObject);
break;
case "Key":
col.gameObject.GetComponent<PlayerStats>().hasKey = true;
Destroy(gameObject);
break;
}
}
}
IEnumerator WaitToKill(float timeToKill)
{
yield return new WaitForSeconds(timeToKill);
Destroy(gameObject);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spring : MonoBehaviour
{
[SerializeField] float springPower = 10f;
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.CompareTag("Player"))
col.gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * springPower, ForceMode2D.Impulse);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovingPlatform : MonoBehaviour
{
public Transform platformPos;
public Transform targetPos;
public Vector2 startPos;
public Vector2 endPos;
private float t = 0.0f;
void Start()
{
startPos = platformPos.transform.position;
endPos = targetPos.transform.position;
}
void Update()
{
// animate the position of the game object...
transform.position = Vector2.Lerp(startPos, endPos, t);
// .. and increase the t interpolater
t += 0.5f * Time.deltaTime;
// now check if the interpolator has reached 1.0
// and swap maximum and minimum so game object moves
// in the opposite direction.
if (t > 1.0f)
{
Vector2 temp = startPos;
startPos = endPos;
endPos = temp;
t = 0.0f;
}
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.CompareTag("Player"))
{
col.gameObject.transform.parent = transform;
}
}
void OnCollisionExit2D(Collision2D col)
{
if(col.gameObject.CompareTag("Player"))
{
col.gameObject.transform.parent = null;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlipPlayer : MonoBehaviour
{
public bool playerFlipped = false;
Vector3 gravity;
public float pGroundOffset = 1;
private Transform pTransform;
private SpriteRenderer pSprite;
private PlayerController pController;
void Awake()
{
pTransform = GetComponent<Transform>();
pSprite = GetComponent<SpriteRenderer>();
pController = GetComponent<PlayerController>();
gravity = Physics2D.gravity;
}
void Update()
{
if(Input.GetButtonDown("Fire2"))
{
Flip();
if(!playerFlipped)
playerFlipped = true;
else
playerFlipped = false;
}
}
void Flip()
{
//flip the gravity
gravity.y *= -1;
Physics2D.gravity = gravity;
//flip player position
pGroundOffset *= -1;
pTransform.position = new Vector2(pTransform.position.x, pTransform.position.y + pGroundOffset);
//flip the sprite
if(pSprite.flipY) pSprite.flipY = false;
else pSprite.flipY = true;
//flip jump direction
pController.jmpStr *= -1;
}
}
2D Platformer Project
2D Platformer Project:
This project is the completion of the game you developed throughout the labs thus far. You will fix any errors, produce supplimental graphics, develop novel elements (mechanics, graphics, etc.), and overall polish the game. Once completed the results should be exported as a final build. The unity project folder and the final game build should be submitted.
You will be graded on the following:
- Coding
- Develop functional code that operates as expected.
- Troubleshoot any errors and optimize the game.
- Graphics
- Develop necessary graphics for the game.
- Clean up and polish graphical assets.
- Novelty
- Develop an innovative, creative elements that separate the game from others.
- Final Build
- Polish and implement solid craftsmanship to develop a “finished” game.
Resources:
- Assignment Video Tutorials
- You may watch the tutorial videos below to help you complete your assignment.
Assignment Video Tutorials
Wait! Before you go!
Did you remember to?
- Read through this webpage
- Submit the 2D Platformer Project on Blackboard