Skip to content

Instantly share code, notes, and snippets.

@CodingDino
Created March 30, 2020 14:12
Show Gist options
  • Save CodingDino/ef3d8295a68ecca5bfd7dd2f6778f42d to your computer and use it in GitHub Desktop.
Save CodingDino/ef3d8295a68ecca5bfd7dd2f6778f42d to your computer and use it in GitHub Desktop.
Updates to Unity Playground UI script (and inspector) to allow hearts for health display and to allow separate win / loss scenes
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
[AddComponentMenu("")]
public class UIScript : MonoBehaviour
{
public enum HealthDisplayType
{
TEXT,
HEART,
//BAR
}
public enum EndGameAction
{
POPUP_TEXT,
CHANGE_SCENE,
//BAR
}
[Header("Configuration")]
public Players numberOfPlayers = Players.OnePlayer;
public GameType gameType = GameType.Score;
// If the scoreToWin is -1, the game becomes endless (no win conditions, but you could do game over)
public int scoreToWin = 5;
public HealthDisplayType displayType = HealthDisplayType.TEXT;
// Heart Icon assets
public Sprite heartIcon;
public Sprite heartContainerIcon;
// Health bar UI object reference
public Sprite healthBarFullSprite;
public Sprite healthBarContainerSprite;
// End game actions
public EndGameAction endGameAction = EndGameAction.POPUP_TEXT;
public const string SAME_SCENE = "0";
public string winScene = SAME_SCENE;
public string loseScene = SAME_SCENE;
[Header("References (don't touch)")]
//Right is used for the score in P1 games
public Text[] numberLabels = new Text[2];
public Text rightLabel, leftLabel;
public Text winLabel;
public GameObject statsPanel, gameOverPanel, winPanel;
public Transform inventory;
public GameObject resourceItemPrefab;
public Image[] healthBarFull = new Image[2];
public Image[] healthBarContainer = new Image[2];
public GameObject[] heartRoots = new GameObject[2];
public GameObject heartPrefab;
public GameObject heartContainerPrefab;
// Internal variables to keep track of score, health, and resources, win state
private int[] scores = new int[2];
private int[] playersHealth = new int[2];
private Dictionary<int, ResourceStruct> resourcesDict = new Dictionary<int, ResourceStruct>(); //holds a reference to all the resources collected, and to their UI
private bool gameOver = false; //this gets changed when the game is won OR lost
private int[] playersHealthMax = new int[2];
private List<Image>[] playerHearts = new List<Image>[2];
private List<Image>[] playerHeartContainers = new List<Image>[2];
private void Start()
{
if(numberOfPlayers == Players.OnePlayer)
{
if (displayType != HealthDisplayType.TEXT)
{
leftLabel.text = "";
numberLabels[0].text = "";
if (displayType == HealthDisplayType.HEART)
{
heartRoots[0].SetActive(true);
}
}
}
else
{
if(gameType == GameType.Score)
{
// Show the 2-player score interface
rightLabel.text = leftLabel.text = "Score";
// Show the score as 0 for both players
numberLabels[0].text = numberLabels[1].text = "0";
scores[0] = scores[1] = 0;
}
else
{
// Show the 2-player life interface
if (displayType == HealthDisplayType.TEXT)
{
rightLabel.text = leftLabel.text = "Life";
}
else
{
rightLabel.text = leftLabel.text = "";
numberLabels[0].text = numberLabels[1].text = "";
if (displayType == HealthDisplayType.HEART)
{
heartRoots[0].SetActive(true);
heartRoots[1].SetActive(true);
}
}
// Life will be provided by the PlayerHealth components
}
}
}
//version of the one below with one parameter to be able to connect UnityEvents
public void AddOnePoint(int playerNumber)
{
AddPoints(playerNumber, 1);
}
public void AddPoints(int playerNumber, int amount = 1)
{
scores[playerNumber] += amount;
if(numberOfPlayers == Players.OnePlayer)
{
numberLabels[1].text = scores[playerNumber].ToString(); //with one player, the score is on the right
}
else
{
numberLabels[playerNumber].text = scores[playerNumber].ToString();
}
if(gameType == GameType.Score
&& scores[playerNumber] >= scoreToWin)
{
GameWon(playerNumber);
}
}
//currently unused by other Playground scripts
public void RemoveOnePoint(int playerNumber)
{
scores[playerNumber]--;
if(numberOfPlayers == Players.OnePlayer)
{
numberLabels[1].text = scores[playerNumber].ToString(); //with one player, the score is on the right
}
else
{
numberLabels[playerNumber].text = scores[playerNumber].ToString();
}
}
public void GameWon(int playerNumber)
{
// only set game over UI if game is not over
if (!gameOver)
{
gameOver = true;
switch (endGameAction)
{
case EndGameAction.POPUP_TEXT:
winLabel.text = "Player " + ++playerNumber + " wins!";
statsPanel.SetActive(false);
winPanel.SetActive(true);
break;
case EndGameAction.CHANGE_SCENE:
if (winScene == SAME_SCENE)
{
//just restart the level
SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
}
else
{
//load another scene
SceneManager.LoadScene(winScene, LoadSceneMode.Single);
}
break;
}
}
}
public void GameOver(int playerNumber)
{
// only set game over UI if game is not over
if (!gameOver)
{
gameOver = true;
switch (endGameAction)
{
case EndGameAction.POPUP_TEXT:
statsPanel.SetActive(false);
gameOverPanel.SetActive(true);
break;
case EndGameAction.CHANGE_SCENE:
if (loseScene == SAME_SCENE)
{
//just restart the level
SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
}
else
{
//load another scene
SceneManager.LoadScene(loseScene, LoadSceneMode.Single);
}
break;
}
}
}
public void SetInitialHealth(int amount, int playerNumber)
{
playersHealthMax[playerNumber] = amount;
playersHealth[playerNumber] = amount;
switch (displayType)
{
case HealthDisplayType.TEXT:
break;
case HealthDisplayType.HEART:
playerHearts[playerNumber] = new List<Image>();
playerHeartContainers[playerNumber] = new List<Image>();
for (int i = 0; i < amount; ++i)
{
GameObject newContainer = Instantiate(heartContainerPrefab, heartRoots[playerNumber].transform);
newContainer.GetComponent<Image>().sprite = heartContainerIcon;
playerHeartContainers[playerNumber].Add(newContainer.GetComponent<Image>());
GameObject newHeart = Instantiate(heartPrefab, newContainer.transform);
newHeart.GetComponent<Image>().sprite = heartIcon;
playerHearts[playerNumber].Add(newHeart.GetComponent<Image>());
}
break;
//case HealthDisplayType.BAR:```````````````````````````````````````
// break;
}
SetHealth(amount, playerNumber);
}
public void SetHealth(int amount, int playerNumber)
{
//Debug.Log("Setting health for player " + playerNumber + " to " + amount + " from "+ playersHealth[playerNumber]);
playersHealth[playerNumber] = amount;
switch (displayType)
{
case HealthDisplayType.TEXT:
numberLabels[playerNumber].text = playersHealth[playerNumber].ToString();
break;
case HealthDisplayType.HEART:
for (int i = 0; i < playersHealthMax[playerNumber]; ++i)
{
playerHearts[playerNumber][i].enabled = i < amount;
}
break;
//case HealthDisplayType.BAR:
// healthBarFull[playerNumber].transform.localScale = new Vector3(((float)amount) / ((float)playersHealthMax[playerNumber]), 0, 0);
// break;
}
}
public void ChangeHealth(int change, int playerNumber)
{
//Debug.Log("Changing health for player " + playerNumber + " by " + change);
SetHealth(playersHealth[playerNumber] + change, playerNumber);
if(gameType != GameType.Endless
&& playersHealth[playerNumber] <= 0)
{
GameOver(playerNumber);
}
}
//Adds a resource to the dictionary, and to the UI
public void AddResource(int resourceType, int pickedUpAmount, Sprite graphics)
{
if(resourcesDict.ContainsKey(resourceType))
{
//update the dictionary key
int newAmount = resourcesDict[resourceType].amount + pickedUpAmount;
resourcesDict[resourceType].UIItem.ShowNumber(newAmount);
resourcesDict[resourceType].amount = newAmount;
}
else
{
//create the UIItemScript and display the icon
UIItemScript newUIItem = Instantiate<GameObject>(resourceItemPrefab).GetComponent<UIItemScript>();
newUIItem.transform.SetParent(inventory, false);
resourcesDict.Add(resourceType, new ResourceStruct(pickedUpAmount, newUIItem));
resourcesDict[resourceType].UIItem.ShowNumber(pickedUpAmount);
resourcesDict[resourceType].UIItem.DisplayIcon(graphics);
}
}
//checks if a certain resource is in the inventory, in the needed quantity
public bool CheckIfHasResources(int resourceType, int amountNeeded = 1)
{
if(resourcesDict.ContainsKey(resourceType))
{
if(resourcesDict[resourceType].amount >= amountNeeded)
{
return true;
}
else
{
//not enough
return false;
}
}
else
{
//resource not present
return false;
}
}
//to use only before checking that the resource is in the dictionary
public void ConsumeResource(int resourceType, int amountNeeded = 1)
{
resourcesDict[resourceType].amount -= amountNeeded;
resourcesDict[resourceType].UIItem.ShowNumber(resourcesDict[resourceType].amount);
}
public enum Players
{
OnePlayer = 0,
TwoPlayers
}
public enum GameType
{
Score = 0,
Life,
Endless
}
}
//just a virtual representation of the resources for the private dictionary
public class ResourceStruct
{
public int amount;
public UIItemScript UIItem;
public ResourceStruct(int a, UIItemScript uiRef)
{
amount = a;
UIItem = uiRef;
}
}
using UnityEngine;
using UnityEditor;
using System.Collections;
[CustomEditor(typeof(UIScript))]
public class UIScriptInspector : InspectorBase
{
private string explanation = "Use the UI to visualise points and health for the players.";
private string lifeReminder = "Don't forget to use the script HealthSystemAttribute on the player(s)!";
private int nOfPlayers = 0, gameType = 0;
private string[] readablePlayerEnum = new string[]{"One player", "Two players"};
private string[] readableGameTypesEnum = new string[]{"Score", "Life", "Endless"};
private string sceneWarning = "WARNING: Make sure the scene is enabled in the Build Settings scenes list.";
private string sceneInfo = "WARNING; To add a new level, save a Unity scene and then go to File > Build Settings... and add the scene to the list.";
public override void OnInspectorGUI()
{
GUILayout.Space(10);
EditorGUILayout.HelpBox(explanation, MessageType.Info);
nOfPlayers = serializedObject.FindProperty("numberOfPlayers").intValue;
gameType = serializedObject.FindProperty("gameType").intValue;
nOfPlayers = EditorGUILayout.Popup("Number of players", nOfPlayers, readablePlayerEnum);
gameType = EditorGUILayout.Popup("Game type", gameType, readableGameTypesEnum);
if(gameType == 0) //score game
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("scoreToWin"));
}
if(gameType == 1) //life
{
EditorGUILayout.HelpBox(lifeReminder, MessageType.Info);
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("displayType"));
UIScript.HealthDisplayType displayType = (UIScript.HealthDisplayType)serializedObject.FindProperty("displayType").intValue;
switch (displayType)
{
case UIScript.HealthDisplayType.TEXT:
break;
case UIScript.HealthDisplayType.HEART:
EditorGUILayout.PropertyField(serializedObject.FindProperty("heartIcon"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("heartContainerIcon"));
break;
/*case UIScript.HealthDisplayType.BAR:
EditorGUILayout.PropertyField(serializedObject.FindProperty("healthBarFull"), true);
EditorGUILayout.PropertyField(serializedObject.FindProperty("healthBarContainer"), true);
break;*/
}
// Scenes
EditorGUILayout.PropertyField(serializedObject.FindProperty("endGameAction"));
UIScript.EndGameAction endGameAction = (UIScript.EndGameAction)serializedObject.FindProperty("endGameAction").intValue;
if (endGameAction == UIScript.EndGameAction.CHANGE_SCENE)
{
bool displayWarning = false;
if (EditorBuildSettings.scenes.Length > 0)
{
int sceneIdWin = 0;
int sceneIdLose = 0;
string sceneNamePropertyWin = serializedObject.FindProperty("winScene").stringValue;
string sceneNamePropertyLose = serializedObject.FindProperty("loseScene").stringValue;
//get available scene names and clean the names
string[] sceneNames = new string[EditorBuildSettings.scenes.Length + 1];
sceneNames[0] = "RELOAD LEVEL";
int i = 1;
foreach (EditorBuildSettingsScene s in EditorBuildSettings.scenes)
{
int lastSlash = s.path.LastIndexOf("/");
string shortPath = s.path.Substring(lastSlash + 1, s.path.Length - 7 - lastSlash);
sceneNames[i] = shortPath;
if (shortPath == sceneNamePropertyWin)
{
sceneIdWin = i;
if (!s.enabled)
{
displayWarning = true;
}
}
if (shortPath == sceneNamePropertyLose)
{
sceneIdLose = i;
if (!s.enabled)
{
displayWarning = true;
}
}
i++;
}
//Display the selector
sceneIdWin = EditorGUILayout.Popup("Winning scene", sceneIdWin, sceneNames);
sceneIdLose = EditorGUILayout.Popup("Losing scene", sceneIdLose, sceneNames);
if (displayWarning)
{
EditorGUILayout.HelpBox(sceneWarning, MessageType.Warning);
}
if (sceneIdWin == 0)
{
serializedObject.FindProperty("winScene").stringValue = UIScript.SAME_SCENE; //this means same scene
}
else
{
serializedObject.FindProperty("winScene").stringValue = sceneNames[sceneIdWin];
}
if (sceneIdLose == 0)
{
serializedObject.FindProperty("loseScene").stringValue = UIScript.SAME_SCENE; //this means same scene
}
else
{
serializedObject.FindProperty("loseScene").stringValue = sceneNames[sceneIdLose];
}
}
else
{
EditorGUILayout.Popup("Winning scene", 0, new string[] { "No scenes available!" });
EditorGUILayout.Popup("Losing scene", 0, new string[] { "No scenes available!" });
EditorGUILayout.HelpBox(sceneInfo, MessageType.Warning);
}
}
//write all the properties back
serializedObject.FindProperty("gameType").intValue = gameType;
serializedObject.FindProperty("numberOfPlayers").intValue = nOfPlayers;
if(GUI.changed)
{
serializedObject.ApplyModifiedProperties();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment