RTS Game

This was my first big Unity project I had to make without a YouTube tutorial. I’m know at a point where I can recognize my mistakes, find and correct them. I still used a text based tutorial on the internet to make the most part of this game. I had to tweak some parts so i could make it the RTS that i want. This game is a simple RTS with not much focus on the graphics but purely on the code and that it works. There’s two ways to win: by gathering a specific amount of money, or by destroying every unit and building from the opponent. The player can select and control his units with the LMB or deselect them by using the RMB. There’s two types of buildings which the Player can build with his Worker unit. The main building is the Warfactory which takes the longest and costs the most to build. It's also the building that can make the Tanks, the Upgraded Tanks and the Worker. The second building is the Refinery. The Refinery can build the harvester which is used to collect resources and then return to the Refinery to store the resources and get Money in return. At first I had trouble making all this because I didn't have the tutorial to help me out, but when I got the tutorial I didn’t face to many troubles. One trouble I had though was that at one point the game broke because I forgot to name the Players differently, but I fixed it and it all works again. All in all, I think it worked out well even though I don’t really like to play RTS games. But it was really fun to learn and see all the things that need to happen in order to get an RTS work like everyone wants it to work.

RTSMenu

RTSGame

RTSSelect

RTSBuilding

Player.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class Player : MonoBehaviour { public string username; public bool human; public HUD hud; public WorldObject SelectedObject { get; set; } public int startMoney, startMoneyLimit, startPower, startPowerLimit; public Material notAllowedMaterial, allowedMaterial; public Color teamColor; private Dictionary<ResourceType, int> resources, resourceLimits; private Building tempBuilding; private Unit tempCreator; private bool findingPlacement = false; /*** Game Engine Methods ***/ void Awake() { resources = InitResourceList(); resourceLimits = InitResourceList(); } void Start() { hud = GetComponentInChildren(); AddStartResourceLimits(); AddStartResources(); } void Update() { if (human) { hud.SetResourceValues(resources, resourceLimits); if (findingPlacement) { tempBuilding.CalculateBounds(); if (CanPlaceBuilding()) tempBuilding.SetTransparentMaterial(allowedMaterial, false); else tempBuilding.SetTransparentMaterial(notAllowedMaterial, false); } } } /*** Private Worker Methods ***/ private Dictionary<ResourceType, int> InitResourceList() { Dictionary<ResourceType, int> list = new Dictionary<ResourceType, int>(); list.Add(ResourceType.Money, 0); list.Add(ResourceType.Power, 0); return list; } private void AddStartResourceLimits() { IncrementResourceLimit(ResourceType.Money, startMoneyLimit); IncrementResourceLimit(ResourceType.Power, startPowerLimit); } private void AddStartResources() { AddResource(ResourceType.Money, startMoney); AddResource(ResourceType.Power, startPower); } /*** Public Methods ***/ public void AddResource(ResourceType type, int amount) { resources[type] += amount; } public void IncrementResourceLimit(ResourceType type, int amount) { resourceLimits[type] += amount; } public void AddUnit(string unitName, Vector3 spawnPoint, Vector3 rallyPoint, Quaternion rotation, Building creator) { Units units = GetComponentInChildren(); GameObject newUnit = (GameObject)Instantiate(ResourceManager.GetUnit(unitName), spawnPoint, rotation); newUnit.transform.parent = units.transform; Unit unitObject = newUnit.GetComponent(); if (unitObject) { unitObject.SetBuilding(creator); if (spawnPoint != rallyPoint) unitObject.StartMove(rallyPoint); } else Destroy(newUnit); } public void CreateBuilding(string buildingName, Vector3 buildPoint, Unit creator, Rect playingArea) { GameObject newBuilding = (GameObject)Instantiate(ResourceManager.GetBuilding(buildingName), buildPoint, new Quaternion()); tempBuilding = newBuilding.GetComponent(); if (tempBuilding) { tempCreator = creator; findingPlacement = true; tempBuilding.SetTransparentMaterial(notAllowedMaterial, true); tempBuilding.SetColliders(false); tempBuilding.SetPlayingArea(playingArea); } else Destroy(newBuilding); } public void FindBuildingLocation() { Vector3 newLocation = WorkManager.FindHitPoint(Input.mousePosition); newLocation.y = 0; tempBuilding.transform.position = newLocation; } public void StartConstruction() { findingPlacement = false; Buildings buildings = GetComponentInChildren(); if (buildings) tempBuilding.transform.parent = buildings.transform; tempBuilding.SetPlayer(); tempBuilding.SetColliders(true); tempCreator.SetBuilding(tempBuilding); tempBuilding.StartConstruction(); RemoveResource(ResourceType.Money, tempBuilding.cost); } public void CancelBuildingPlacement() { findingPlacement = false; Destroy(tempBuilding.gameObject); tempBuilding = null; tempCreator = null; } public void RemoveResource(ResourceType type, int amount) { resources[type] -= amount; } public bool CanPlaceBuilding() { bool canPlace = true; Bounds placeBounds = tempBuilding.GetSelectionBounds(); //shorthand for the coordinates of the center of the selection bounds float cx = placeBounds.center.x; float cy = placeBounds.center.y; float cz = placeBounds.center.z; //shorthand for the coordinates of the extents of the selection box float ex = placeBounds.extents.x; float ey = placeBounds.extents.y; float ez = placeBounds.extents.z; //Determine the screen coordinates for the corners of the selection bounds List corners = new List(); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz - ez))); foreach (Vector3 corner in corners) { GameObject hitObject = WorkManager.FindHitObject(corner); if (hitObject && hitObject.name != "Ground") { WorldObject worldObject = hitObject.transform.parent.GetComponent(); if (worldObject && placeBounds.Intersects(worldObject.GetSelectionBounds())) canPlace = false; } } return canPlace; } public bool IsFindingBuildingLocation() { return findingPlacement; } public bool IsDead() { Building[] buildings = GetComponentsInChildren(); Unit[] units = GetComponentsInChildren(); if (buildings != null && buildings.Length > 0) return false; if (units != null && units.Length > 0) return false; return true; } public int GetResourceAmount(ResourceType type) { return resources[type]; } } UserInput.cs
using UnityEngine; using System.Collections; using RTS; public class UserInput : MonoBehaviour { private Player player; // Use this for initialization void Start() { player = transform.root.GetComponent(); } // Update is called once per frame void Update() { if (player && player.human) { if (Input.GetKeyDown(KeyCode.Escape)) OpenPauseMenu(); MoveCamera(); RotateCamera(); MouseActivity(); } } private void OpenPauseMenu() { RightMouseClick(); Time.timeScale = 0.0f; GetComponentInChildren().enabled = true; GetComponent().enabled = false; UnityEngine.Cursor.visible = true; ResourceManager.MenuOpen = true; } private void MoveCamera() { float xpos = Input.mousePosition.x; float ypos = Input.mousePosition.y; Vector3 movement = new Vector3(0, 0, 0); bool mouseScroll = false; //horizontal camera movement if (xpos >= 0 && xpos < ResourceManager.ScrollWidth) { movement.x -= ResourceManager.ScrollSpeed; player.hud.SetCursorState(CursorState.PanLeft); mouseScroll = true; } else if (xpos <= Screen.width && xpos > Screen.width - ResourceManager.ScrollWidth) { movement.x += ResourceManager.ScrollSpeed; player.hud.SetCursorState(CursorState.PanRight); mouseScroll = true; } //vertical camera movement if (ypos >= 0 && ypos < ResourceManager.ScrollWidth) { movement.z -= ResourceManager.ScrollSpeed; player.hud.SetCursorState(CursorState.PanDown); mouseScroll = true; } else if (ypos <= Screen.height && ypos > Screen.height - ResourceManager.ScrollWidth) { movement.z += ResourceManager.ScrollSpeed; player.hud.SetCursorState(CursorState.PanUp); mouseScroll = true; } //make sure movement is in the direction the camera is pointing //but ignore the vertical tilt of the camera to get sensible scrolling movement = Camera.main.transform.TransformDirection(movement); movement.y = 0; //away from ground movement movement.y -= ResourceManager.ScrollSpeed * Input.GetAxis("Mouse ScrollWheel"); //calculate desired camera position based on received input Vector3 origin = Camera.main.transform.position; Vector3 destination = origin; destination.x += movement.x; destination.y += movement.y; destination.z += movement.z; //limit away from ground movement to be between a minimum and maximum distance if (destination.y > ResourceManager.MaxCameraHeight) { destination.y = ResourceManager.MaxCameraHeight; } else if (destination.y < ResourceManager.MinCameraHeight) { destination.y = ResourceManager.MinCameraHeight; } //if a change in position is detected perform the necessary update if (destination != origin) { Camera.main.transform.position = Vector3.MoveTowards(origin, destination, Time.deltaTime * ResourceManager.ScrollSpeed); } //set cursor back to default state it should be in if (!mouseScroll) { player.hud.SetCursorState(CursorState.Select); } } private void RotateCamera() { Vector3 origin = Camera.main.transform.eulerAngles; Vector3 destination = origin; //detect rotation amount if ALT is being held and the Right mouse button is down if ((Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) && Input.GetMouseButton(1)) { destination.x -= Input.GetAxis("Mouse Y") * ResourceManager.RotateAmount; destination.y += Input.GetAxis("Mouse X") * ResourceManager.RotateAmount; } //if a change in position is detected perform the necessary update if (destination != origin) { Camera.main.transform.eulerAngles = Vector3.MoveTowards(origin, destination, Time.deltaTime * ResourceManager.RotateSpeed); } } private void MouseActivity() { if (Input.GetMouseButtonDown(0)) LeftMouseClick(); else if (Input.GetMouseButtonDown(1)) RightMouseClick(); MouseHover(); } private void LeftMouseClick() { if (player.hud.MouseInBounds()) { if (player.IsFindingBuildingLocation()) { if (player.CanPlaceBuilding()) player.StartConstruction(); } else { GameObject hitObject = WorkManager.FindHitObject(Input.mousePosition); Vector3 hitPoint = WorkManager.FindHitPoint(Input.mousePosition); if (hitObject && hitPoint != ResourceManager.InvalidPosition) { if (player.SelectedObject) player.SelectedObject.MouseClick(hitObject, hitPoint, player); else if (hitObject.name != "Ground") { WorldObject worldObject = hitObject.transform.parent.GetComponent(); if (worldObject) { //we already know the player has no selected object player.SelectedObject = worldObject; worldObject.SetSelection(true, player.hud.GetPlayingArea()); } } } } } } private void RightMouseClick() { if (player.hud.MouseInBounds() && !Input.GetKey(KeyCode.LeftAlt) && player.SelectedObject) { if (player.IsFindingBuildingLocation()) { player.CancelBuildingPlacement(); } else { player.SelectedObject.SetSelection(false, player.hud.GetPlayingArea()); player.SelectedObject = null; } } } private void MouseHover() { if (player.hud.MouseInBounds()) { if (player.IsFindingBuildingLocation()) { player.FindBuildingLocation(); } else { GameObject hoverObject = WorkManager.FindHitObject(Input.mousePosition); if (hoverObject) { if (player.SelectedObject) player.SelectedObject.SetHoverState(hoverObject); else if (hoverObject.name != "Ground") { Player owner = hoverObject.transform.root.GetComponent(); if (owner) { Unit unit = hoverObject.transform.parent.GetComponent(); Building building = hoverObject.transform.parent.GetComponent(); if (owner.username == player.username && (unit || building)) player.hud.SetCursorState(CursorState.Select); } } } } } } } HUD.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class HUD : MonoBehaviour { public GUISkin resourceSkin, ordersSkin, selectBoxSkin, mouseCursorSkin; public Texture2D activeCursor; public Texture2D selectCursor, leftCursor, rightCursor, upCursor, downCursor, rallyPointCursor; public Texture2D[] attackCursors, harvestCursors, moveCursors; public Texture2D[] resources, resourceHealthBars; public Texture2D buttonHover, buttonClick, smallButtonHover, smallButtonClick; public Texture2D buildFrame, buildMask; public Texture2D healthy, damaged, critical; private Player player; private CursorState activeCursorState, previousCursorState; private int currentFrame = 0, buildAreaHeight = 0; private Dictionary<ResourceType, int> resourceValues, resourceLimits; private Dictionary<ResourceType, Texture2D> resourceImages; private WorldObject lastSelection; private float sliderValue; private const int ORDERS_BAR_WIDTH = 150, RESOURCE_BAR_HEIGHT = 40; private const int SELECTION_NAME_HEIGHT = 19, SCROLL_BAR_WIDTH = 22, BUTTON_SPACING = 7; private const int ICON_WIDTH = 32, ICON_HEIGHT = 32, TEXT_WIDTH = 128, TEXT_HEIGHT = 32; private const int BUILD_IMAGE_WIDTH = 64, BUILD_IMAGE_HEIGHT = 64, BUILD_IMAGE_PADDING = 8; /*** Game Engine Methods ***/ void Start() { player = transform.root.GetComponent(); resourceValues = new Dictionary<ResourceType, int>(); resourceLimits = new Dictionary<ResourceType, int>(); resourceImages = new Dictionary<ResourceType, Texture2D>(); for (int i = 0; i < resources.Length; i++) { switch (resources[i].name) { case "Money": resourceImages.Add(ResourceType.Money, resources[i]); resourceValues.Add(ResourceType.Money, 0); resourceLimits.Add(ResourceType.Money, 0); break; case "Power": resourceImages.Add(ResourceType.Power, resources[i]); resourceValues.Add(ResourceType.Power, 0); resourceLimits.Add(ResourceType.Power, 0); break; default: break; } } Dictionary<ResourceType, Texture2D> resourceHealthBarTextures = new Dictionary<ResourceType, Texture2D>(); for (int i = 0; i < resourceHealthBars.Length; i++) { switch (resourceHealthBars[i].name) { case "ore": resourceHealthBarTextures.Add(ResourceType.Ore, resourceHealthBars[i]); break; default: break; } } ResourceManager.SetResourceHealthBarTextures(resourceHealthBarTextures); buildAreaHeight = Screen.height - RESOURCE_BAR_HEIGHT - SELECTION_NAME_HEIGHT - 2 * BUTTON_SPACING; ResourceManager.StoreSelectBoxItems(selectBoxSkin, healthy, damaged, critical); SetCursorState(CursorState.Select); } void OnGUI() { //we only want to draw a GUI for human players if (player.human) { DrawOrdersBar(); DrawResourceBar(); //call last to ensure that the custom mouse cursor is seen on top of everything if (ResourceManager.MenuOpen) { UnityEngine.Cursor.visible = true; } else DrawMouseCursor(); } } /*** Public methods for interacting with the HUD ***/ public bool MouseInBounds() { //Screen coordinates start in the lower-left corner of the screen //not the top-right of the screen like the drawing coordinates do Vector3 mousePos = Input.mousePosition; bool insideWidth = mousePos.x >= 0 && mousePos.x <= Screen.width - ORDERS_BAR_WIDTH; bool insideHeight = mousePos.y >= 0 && mousePos.y <= Screen.height - RESOURCE_BAR_HEIGHT; return insideWidth && insideHeight; } public Rect GetPlayingArea() { return new Rect(0, RESOURCE_BAR_HEIGHT, Screen.width - ORDERS_BAR_WIDTH, Screen.height - RESOURCE_BAR_HEIGHT); } public void SetCursorState(CursorState newState) { if (activeCursorState != newState) previousCursorState = activeCursorState; activeCursorState = newState; switch (newState) { case CursorState.Select: activeCursor = selectCursor; break; case CursorState.Attack: currentFrame = (int)Time.time % attackCursors.Length; activeCursor = attackCursors[currentFrame]; break; case CursorState.Harvest: currentFrame = (int)Time.time % harvestCursors.Length; activeCursor = harvestCursors[currentFrame]; break; case CursorState.Move: currentFrame = (int)Time.time % moveCursors.Length; activeCursor = moveCursors[currentFrame]; break; case CursorState.PanLeft: activeCursor = leftCursor; break; case CursorState.PanRight: activeCursor = rightCursor; break; case CursorState.PanUp: activeCursor = upCursor; break; case CursorState.PanDown: activeCursor = downCursor; break; case CursorState.RallyPoint: activeCursor = rallyPointCursor; break; default: break; } } public void SetResourceValues(Dictionary<ResourceType, int> resourceValues, Dictionary<ResourceType, int> resourceLimits) { this.resourceValues = resourceValues; this.resourceLimits = resourceLimits; } public CursorState GetCursorState() { return activeCursorState; } public CursorState GetPreviousCursorState() { return previousCursorState; } /*** Private Worker Methods ***/ private void DrawOrdersBar() { GUI.skin = ordersSkin; GUI.BeginGroup(new Rect(Screen.width - ORDERS_BAR_WIDTH - BUILD_IMAGE_WIDTH, RESOURCE_BAR_HEIGHT, ORDERS_BAR_WIDTH + BUILD_IMAGE_WIDTH, Screen.height - RESOURCE_BAR_HEIGHT)); GUI.Box(new Rect(BUILD_IMAGE_WIDTH + SCROLL_BAR_WIDTH, 0, ORDERS_BAR_WIDTH, Screen.height - RESOURCE_BAR_HEIGHT), ""); string selectionName = ""; if (player.SelectedObject) { selectionName = player.SelectedObject.objectName; if (player.SelectedObject.IsOwnedBy(player)) { //reset slider value if the selected object has changed if (lastSelection && lastSelection != player.SelectedObject) sliderValue = 0.0f; if (player.SelectedObject.IsActive) DrawActions(player.SelectedObject.GetActions()); //store the current selection lastSelection = player.SelectedObject; Building selectedBuilding = lastSelection.GetComponent(); if (selectedBuilding) { DrawBuildQueue(selectedBuilding.getBuildQueueValues(), selectedBuilding.getBuildPercentage()); DrawStandardBuildingOptions(selectedBuilding); } } } if (!selectionName.Equals("")) { int leftPos = BUILD_IMAGE_WIDTH + SCROLL_BAR_WIDTH + 15; int topPos = buildAreaHeight + BUTTON_SPACING; GUI.Label(new Rect(leftPos, topPos, ORDERS_BAR_WIDTH, SELECTION_NAME_HEIGHT), selectionName); } GUI.EndGroup(); } private void DrawActions(string[] actions) { GUIStyle buttons = new GUIStyle(); buttons.hover.background = buttonHover; buttons.active.background = buttonClick; GUI.skin.button = buttons; int numActions = actions.Length; //define the area to draw the actions inside GUI.BeginGroup(new Rect(BUILD_IMAGE_WIDTH, 0, ORDERS_BAR_WIDTH, buildAreaHeight)); //draw scroll bar for the list of actions if need be if (numActions >= MaxNumRows(buildAreaHeight)) DrawSlider(buildAreaHeight, numActions / 2.0f); //display possible actions as buttons and handle the button click for each for (int i = 0; i < numActions; i++) { int column = i % 2; int row = i / 2; Rect pos = GetButtonPos(row, column); Texture2D action = ResourceManager.GetBuildImage(actions[i]); if (action) { //create the button and handle the click of that button if (GUI.Button(pos, action)) { if (player.SelectedObject) player.SelectedObject.PerformAction(actions[i]); } } } GUI.EndGroup(); } private int MaxNumRows(int areaHeight) { return areaHeight / BUILD_IMAGE_HEIGHT; } private Rect GetButtonPos(int row, int column) { int left = SCROLL_BAR_WIDTH + column * BUILD_IMAGE_WIDTH; float top = row * BUILD_IMAGE_HEIGHT - sliderValue * BUILD_IMAGE_HEIGHT; return new Rect(left, top, BUILD_IMAGE_WIDTH, BUILD_IMAGE_HEIGHT); } private void DrawSlider(int groupHeight, float numRows) { //slider goes from 0 to the number of rows that do not fit on screen sliderValue = GUI.VerticalSlider(GetScrollPos(groupHeight), sliderValue, 0.0f, numRows - MaxNumRows(groupHeight)); } private Rect GetScrollPos(int groupHeight) { return new Rect(BUTTON_SPACING, BUTTON_SPACING, SCROLL_BAR_WIDTH, groupHeight - 2 * BUTTON_SPACING); } private void DrawBuildQueue(string[] buildQueue, float buildPercentage) { for (int i = 0; i < buildQueue.Length; i++) { float topPos = i * BUILD_IMAGE_HEIGHT - (i + 1) * BUILD_IMAGE_PADDING; Rect buildPos = new Rect(BUILD_IMAGE_PADDING, topPos, BUILD_IMAGE_WIDTH, BUILD_IMAGE_HEIGHT); GUI.DrawTexture(buildPos, ResourceManager.GetBuildImage(buildQueue[i])); GUI.DrawTexture(buildPos, buildFrame); topPos += BUILD_IMAGE_PADDING; float width = BUILD_IMAGE_WIDTH - 2 * BUILD_IMAGE_PADDING; float height = BUILD_IMAGE_HEIGHT - 2 * BUILD_IMAGE_PADDING; if (i == 0) { //shrink the build mask on the item currently being built to give an idea of progress topPos += height * buildPercentage; height *= (1 - buildPercentage); } GUI.DrawTexture(new Rect(2 * BUILD_IMAGE_PADDING, topPos, width, height), buildMask); } } private void DrawStandardBuildingOptions(Building building) { GUIStyle buttons = new GUIStyle(); buttons.hover.background = smallButtonHover; buttons.active.background = smallButtonClick; GUI.skin.button = buttons; int leftPos = BUILD_IMAGE_WIDTH + SCROLL_BAR_WIDTH + BUTTON_SPACING; int topPos = buildAreaHeight - BUILD_IMAGE_HEIGHT / 2; int width = BUILD_IMAGE_WIDTH / 2; int height = BUILD_IMAGE_HEIGHT / 2; if (GUI.Button(new Rect(leftPos, topPos, width, height), building.sellImage)) { building.Sell(); } if (building.hasSpawnPoint()) { leftPos += width + BUTTON_SPACING; if (GUI.Button(new Rect(leftPos, topPos, width, height), building.rallyPointImage)) { if (activeCursorState != CursorState.RallyPoint && previousCursorState != CursorState.RallyPoint) SetCursorState(CursorState.RallyPoint); else { //dirty hack to ensure toggle between RallyPoint and not works ... SetCursorState(CursorState.PanRight); SetCursorState(CursorState.Select); } } } } private void DrawResourceBar() { GUI.skin = resourceSkin; GUI.BeginGroup(new Rect(0, 0, Screen.width, RESOURCE_BAR_HEIGHT)); GUI.Box(new Rect(0, 0, Screen.width, RESOURCE_BAR_HEIGHT), ""); int topPos = 4, iconLeft = 4, textLeft = 20; DrawResourceIcon(ResourceType.Money, iconLeft, textLeft, topPos); iconLeft += TEXT_WIDTH; textLeft += TEXT_WIDTH; DrawResourceIcon(ResourceType.Power, iconLeft, textLeft, topPos); GUI.EndGroup(); } private void DrawResourceIcon(ResourceType type, int iconLeft, int textLeft, int topPos) { Texture2D icon = resourceImages[type]; string text = resourceValues[type].ToString() + "/" + resourceLimits[type].ToString(); GUI.DrawTexture(new Rect(iconLeft, topPos, ICON_WIDTH, ICON_HEIGHT), icon); GUI.Label(new Rect(textLeft, topPos, TEXT_WIDTH, TEXT_HEIGHT), text); } private void DrawMouseCursor() { bool mouseOverHud = !MouseInBounds() && activeCursorState != CursorState.PanRight && activeCursorState != CursorState.PanUp; if (mouseOverHud) { UnityEngine.Cursor.visible = true; } else { UnityEngine.Cursor.visible = false; if (!player.IsFindingBuildingLocation()) { GUI.skin = mouseCursorSkin; GUI.BeginGroup(new Rect(0, 0, Screen.width, Screen.height)); UpdateCursorAnimation(); Rect cursorPosition = GetCursorDrawPosition(); GUI.Label(cursorPosition, activeCursor); GUI.EndGroup(); } } } private void UpdateCursorAnimation() { //sequence animation for cursor (based on more than one image for the cursor) //change once per second, loops through array of images if (activeCursorState == CursorState.Move) { currentFrame = (int)Time.time % moveCursors.Length; activeCursor = moveCursors[currentFrame]; } else if (activeCursorState == CursorState.Attack) { currentFrame = (int)Time.time % attackCursors.Length; activeCursor = attackCursors[currentFrame]; } else if (activeCursorState == CursorState.Harvest) { currentFrame = (int)Time.time % harvestCursors.Length; activeCursor = harvestCursors[currentFrame]; } } private Rect GetCursorDrawPosition() { //set base position for custom cursor image float leftPos = Input.mousePosition.x; float topPos = Screen.height - Input.mousePosition.y; //screen draw coordinates are inverted //adjust position base on the type of cursor being shown if (activeCursorState == CursorState.PanRight) leftPos = Screen.width - activeCursor.width; else if (activeCursorState == CursorState.PanDown) topPos = Screen.height - activeCursor.height; else if (activeCursorState == CursorState.Move || activeCursorState == CursorState.Select || activeCursorState == CursorState.Harvest) { topPos -= activeCursor.height / 2; leftPos -= activeCursor.width / 2; } else if (activeCursorState == CursorState.RallyPoint) topPos -= activeCursor.height; return new Rect(leftPos, topPos, activeCursor.width, activeCursor.height); } } ResourceManager.cs
using UnityEngine; using System.Collections.Generic; namespace RTS { public static class ResourceManager { public static int ScrollWidth { get { return 15; } } public static float ScrollSpeed { get { return 25; } } public static float RotateAmount { get { return 10; } } public static float RotateSpeed { get { return 100; } } public static float MinCameraHeight { get { return 10; } } public static float MaxCameraHeight { get { return 40; } } private static Vector3 invalidPosition = new Vector3(-99999, -99999, -99999); private static Bounds invalidBounds = new Bounds(new Vector3(-99999, -99999, -99999), new Vector3(0, 0, 0)); public static Vector3 InvalidPosition { get { return invalidPosition; } } public static Bounds InvalidBounds { get { return invalidBounds; } } public static bool MenuOpen { get; set; } private static float buttonHeight = 40; private static float headerHeight = 32, headerWidth = 256; private static float textHeight = 25, padding = 10; public static float PauseMenuHeight { get { return headerHeight + 2 * buttonHeight + 4 * padding; } } public static float MenuWidth { get { return headerWidth + 2 * padding; } } public static float ButtonHeight { get { return buttonHeight; } } public static float ButtonWidth { get { return (MenuWidth - 3 * padding) / 2; } } public static float HeaderHeight { get { return headerHeight; } } public static float HeaderWidth { get { return headerWidth; } } public static float TextHeight { get { return textHeight; } } public static float Padding { get { return padding; } } private static GUISkin selectBoxSkin; public static GUISkin SelectBoxSkin { get { return selectBoxSkin; } } private static Texture2D healthyTexture, damagedTexture, criticalTexture; public static Texture2D HealthyTexture { get { return healthyTexture; } } public static Texture2D DamagedTexture { get { return damagedTexture; } } public static Texture2D CriticalTexture { get { return criticalTexture; } } private static Dictionary<ResourceType, Texture2D> resourceHealthBarTextures; public static void StoreSelectBoxItems(GUISkin skin, Texture2D healthy, Texture2D damaged, Texture2D critical) { selectBoxSkin = skin; healthyTexture = healthy; damagedTexture = damaged; criticalTexture = critical; } public static int BuildSpeed { get { return 2; } } private static GameObjectList gameObjectList; public static void SetGameObjectList(GameObjectList objectList) { gameObjectList = objectList; } public static GameObject GetBuilding(string name) { return gameObjectList.GetBuilding(name); } public static GameObject GetUnit(string name) { return gameObjectList.GetUnit(name); } public static GameObject GetWorldObject(string name) { return gameObjectList.GetWorldObject(name); } public static GameObject GetPlayerObject() { return gameObjectList.GetPlayerObject(); } public static Texture2D GetBuildImage(string name) { return gameObjectList.GetBuildImage(name); } public static void SetResourceHealthBarTextures(Dictionary<ResourceType, Texture2D> images) { resourceHealthBarTextures = images; } public static Texture2D GetResourceHealthBar(ResourceType resourceType) { if (resourceHealthBarTextures != null && resourceHealthBarTextures.ContainsKey(resourceType)) return resourceHealthBarTextures[resourceType]; return null; } } } WorkManager.cs
using UnityEngine; using System.Collections.Generic; namespace RTS { public static class WorkManager { public static Rect CalculateSelectionBox(Bounds selectionBounds, Rect playingArea) { //shorthand for the coordinates of the centre of the selection bounds float cx = selectionBounds.center.x; float cy = selectionBounds.center.y; float cz = selectionBounds.center.z; //shorthand for the coordinates of the extents of the selection bounds float ex = selectionBounds.extents.x; float ey = selectionBounds.extents.y; float ez = selectionBounds.extents.z; //Determine the screen coordinates for the corners of the selection bounds List corners = new List(); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy + ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx + ex, cy - ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz + ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy + ey, cz - ez))); corners.Add(Camera.main.WorldToScreenPoint(new Vector3(cx - ex, cy - ey, cz - ez))); //Determine the bounds on screen for the selection bounds Bounds screenBounds = new Bounds(corners[0], Vector3.zero); for (int i = 1; i < corners.Count; i++) { screenBounds.Encapsulate(corners[i]); } //Screen coordinates start in the bottom right corner, rather than the top left corner //this correction is needed to make sure the selection box is drawn in the correct place float selectBoxTop = playingArea.height - (screenBounds.center.y + screenBounds.extents.y); float selectBoxLeft = screenBounds.center.x - screenBounds.extents.x; float selectBoxWidth = 2 * screenBounds.extents.x; float selectBoxHeight = 2 * screenBounds.extents.y; return new Rect(selectBoxLeft, selectBoxTop, selectBoxWidth, selectBoxHeight); } public static GameObject FindHitObject(Vector3 origin) { Ray ray = Camera.main.ScreenPointToRay(origin); RaycastHit hit; if (Physics.Raycast(ray, out hit)) return hit.collider.gameObject; return null; } public static Vector3 FindHitPoint(Vector3 origin) { Ray ray = Camera.main.ScreenPointToRay(origin); RaycastHit hit; if (Physics.Raycast(ray, out hit)) return hit.point; return ResourceManager.InvalidPosition; } } } WorldObject.cs
using UnityEngine; using RTS; using System.Collections.Generic; public class WorldObject : MonoBehaviour { //Public variables public string objectName = "WorldObject"; public Texture2D buildImage; public int cost = 100, sellValue = 10, hitPoints = 100, maxHitPoints = 100; public float weaponRange = 10.0f, weaponRechargeTime = 1.0f, weaponAimSpeed = 1.0f; public virtual bool IsActive { get { return true; } } public AudioClip attackSound, selectSound, useWeaponSound; public float attackVolume = 1.0f, selectVolume = 1.0f, useWeaponVolume = 1.0f; //Variables accessible by subclass protected Player player; protected string[] actions = { }; protected bool currentlySelected = false; protected Bounds selectionBounds; protected Rect playingArea = new Rect(0.0f, 0.0f, 0.0f, 0.0f); protected GUIStyle healthStyle = new GUIStyle(); protected float healthPercentage = 1.0f; protected WorldObject target = null; protected bool attacking = false, movingIntoPosition = false, aiming = false; protected AudioElement audioElement; //Private variables private List oldMaterials = new List(); private float currentWeaponChargeTime; /*** Game Engine methods, all can be overridden by subclass ***/ protected virtual void Awake() { selectionBounds = ResourceManager.InvalidBounds; CalculateBounds(); currentWeaponChargeTime = weaponRechargeTime; } protected virtual void Start() { SetPlayer(); if (player) SetTeamColor(); InitialiseAudio(); } protected virtual void Update() { currentWeaponChargeTime += Time.deltaTime; if (attacking && !movingIntoPosition && !aiming) PerformAttack(); } protected virtual void OnGUI() { if (currentlySelected && !ResourceManager.MenuOpen) DrawSelection(); } /*** Public methods ***/ public void SetPlayer() { player = transform.root.GetComponentInChildren(); } public virtual void SetSelection(bool selected, Rect playingArea) { currentlySelected = selected; if (selected) { this.playingArea = playingArea; if (audioElement != null) audioElement.Play(selectSound); } } public void SetPlayingArea(Rect playingArea) { this.playingArea = playingArea; } public string[] GetActions() { //should we be checking that the player who owns this is the one who asked for this??? return actions; } public virtual void PerformAction(string actionToPerform) { //it is up to children with specific actions to determine what to do with each of those actions } public virtual void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) { //only handle input if currently selected if (currentlySelected && hitObject && hitObject.name != "Ground") { WorldObject worldObject = hitObject.transform.parent.GetComponent(); //clicked on another selectable object if (worldObject) { Resource resource = hitObject.transform.parent.GetComponent(); if (resource && resource.isEmpty()) return; Player owner = hitObject.transform.root.GetComponent(); if (owner) { //the object is controlled by a player if (player && player.human) { //this object is controlled by a human player //start attack if object is not owned by the same player and this object can attack, else select if (player.username != owner.username && CanAttack()) BeginAttack(worldObject); else ChangeSelection(worldObject, controller); } else ChangeSelection(worldObject, controller); } else ChangeSelection(worldObject, controller); } } } public virtual void SetHoverState(GameObject hoverObject) { //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected) { //something other than the ground is being hovered over if (hoverObject.name != "Ground") { Player owner = hoverObject.transform.root.GetComponent(); Unit unit = hoverObject.transform.parent.GetComponent(); Building building = hoverObject.transform.parent.GetComponent(); if (owner) { //the object is owned by a player if (owner.username == player.username) player.hud.SetCursorState(CursorState.Select); else if (CanAttack()) player.hud.SetCursorState(CursorState.Attack); else player.hud.SetCursorState(CursorState.Select); } else if (unit || building && CanAttack()) player.hud.SetCursorState(CursorState.Attack); else player.hud.SetCursorState(CursorState.Select); } } } public void CalculateBounds() { selectionBounds = new Bounds(transform.position, Vector3.zero); foreach (Renderer r in GetComponentsInChildren()) { selectionBounds.Encapsulate(r.bounds); } } public bool IsOwnedBy(Player owner) { if (player && player.Equals(owner)) { return true; } else { return false; } } public Bounds GetSelectionBounds() { return selectionBounds; } public void SetColliders(bool enabled) { Collider[] colliders = GetComponentsInChildren(); foreach (Collider collider in colliders) collider.enabled = enabled; } public void SetTransparentMaterial(Material material, bool storeExistingMaterial) { if (storeExistingMaterial) oldMaterials.Clear(); Renderer[] renderers = GetComponentsInChildren(); foreach (Renderer renderer in renderers) { if (storeExistingMaterial) oldMaterials.Add(renderer.material); renderer.material = material; } } public void RestoreMaterials() { Renderer[] renderers = GetComponentsInChildren(); if (oldMaterials.Count == renderers.Length) { for (int i = 0; i < renderers.Length; i++) { renderers[i].material = oldMaterials[i]; } } } public virtual bool CanAttack() { //default behaviour needs to be overidden by children return false; } public virtual bool CanMove() { //default behaviour needs to be overidden by children return false; } public void TakeDamage(int damage) { hitPoints -= damage; if (hitPoints <= 0) Destroy(gameObject); } /*** Private worker methods ***/ private void ChangeSelection(WorldObject worldObject, Player controller) { //this should be called by the following line, but there is an outside chance it will not SetSelection(false, playingArea); if (controller.SelectedObject) controller.SelectedObject.SetSelection(false, playingArea); controller.SelectedObject = worldObject; worldObject.SetSelection(true, controller.hud.GetPlayingArea()); } private void DrawSelection() { GUI.skin = ResourceManager.SelectBoxSkin; Rect selectBox = WorkManager.CalculateSelectionBox(selectionBounds, playingArea); //Draw the selection box around the currently selected object, within the bounds of the playing area GUI.BeginGroup(playingArea); DrawSelectionBox(selectBox); GUI.EndGroup(); } private void BeginAttack(WorldObject target) { if (audioElement != null) audioElement.Play(attackSound); this.target = target; if (TargetInRange()) { attacking = true; PerformAttack(); } else AdjustPosition(); } private void PerformAttack() { if (!target) { attacking = false; return; } if (!TargetInRange()) AdjustPosition(); else if (!TargetInFrontOfWeapon()) AimAtTarget(); else if (ReadyToFire()) UseWeapon(); } private bool TargetInRange() { Vector3 targetLocation = target.transform.position; Vector3 direction = targetLocation - transform.position; if (direction.sqrMagnitude < weaponRange * weaponRange) { return true; } return false; } private bool TargetInFrontOfWeapon() { Vector3 targetLocation = target.transform.position; Vector3 direction = targetLocation - transform.position; if (direction.normalized == transform.forward.normalized) return true; else return false; } private Vector3 FindNearestAttackPosition() { Vector3 targetLocation = target.transform.position; Vector3 direction = targetLocation - transform.position; float targetDistance = direction.magnitude; float distanceToTravel = targetDistance - (0.9f * weaponRange); return Vector3.Lerp(transform.position, targetLocation, distanceToTravel / targetDistance); } private void AdjustPosition() { Unit self = this as Unit; if (self) { movingIntoPosition = true; Vector3 attackPosition = FindNearestAttackPosition(); self.StartMove(attackPosition); attacking = true; } else attacking = false; } private bool ReadyToFire() { if (currentWeaponChargeTime >= weaponRechargeTime) return true; return false; } /* Internal worker methods that can be accessed by subclass */ protected virtual void DrawSelectionBox(Rect selectBox) { GUI.Box(selectBox, ""); CalculateCurrentHealth(0.35f, 0.65f); DrawHealthBar(selectBox, ""); } protected virtual void CalculateCurrentHealth(float lowSplit, float highSplit) { healthPercentage = (float)hitPoints / (float)maxHitPoints; if (healthPercentage > highSplit) healthStyle.normal.background = ResourceManager.HealthyTexture; else if (healthPercentage > lowSplit) healthStyle.normal.background = ResourceManager.DamagedTexture; else healthStyle.normal.background = ResourceManager.CriticalTexture; } protected void DrawHealthBar(Rect selectBox, string label) { healthStyle.padding.top = -20; healthStyle.fontStyle = FontStyle.Bold; GUI.Label(new Rect(selectBox.x, selectBox.y - 7, selectBox.width * healthPercentage, 5), label, healthStyle); } protected void SetTeamColor() { TeamColor[] teamColors = GetComponentsInChildren(); foreach (TeamColor teamColor in teamColors) teamColor.GetComponent().material.color = player.teamColor; } protected virtual void UseWeapon() { if (audioElement != null && Time.timeScale > 0) audioElement.Play(useWeaponSound); currentWeaponChargeTime = 0.0f; //this behaviour needs to be specified by a specific object } protected virtual void AimAtTarget() { aiming = true; //this behaviour needs to be specified by a specific object } protected virtual void InitialiseAudio() { List sounds = new List(); List volumes = new List(); if (attackVolume < 0.0f) attackVolume = 0.0f; if (attackVolume > 1.0f) attackVolume = 1.0f; sounds.Add(attackSound); volumes.Add(attackVolume); if (selectVolume < 0.0f) selectVolume = 0.0f; if (selectVolume > 1.0f) selectVolume = 1.0f; sounds.Add(selectSound); volumes.Add(selectVolume); if (useWeaponVolume < 0.0f) useWeaponVolume = 0.0f; if (useWeaponVolume > 1.0f) useWeaponVolume = 1.0f; sounds.Add(useWeaponSound); volumes.Add(useWeaponVolume); audioElement = new AudioElement(sounds, volumes, objectName, this.transform); } } GameObjectList.cs
using UnityEngine; using System.Collections; using RTS; public class GameObjectList : MonoBehaviour { public GameObject[] buildings; public GameObject[] units; public GameObject[] worldObjects; public GameObject player; private static bool created = false; void Awake() { if (!created) { DontDestroyOnLoad(transform.gameObject); ResourceManager.SetGameObjectList(this); created = true; } else { Destroy(this.gameObject); } } public GameObject GetBuilding(string name) { for (int i = 0; i < buildings.Length; i++) { Building building = buildings[i].GetComponent(); if (building && building.name == name) return buildings[i]; } return null; } public GameObject GetUnit(string name) { for (int i = 0; i < units.Length; i++) { Unit unit = units[i].GetComponent(); if (unit && unit.name == name) return units[i]; } return null; } public GameObject GetWorldObject(string name) { foreach (GameObject worldObject in worldObjects) { if (worldObject.name == name) return worldObject; } return null; } public GameObject GetPlayerObject() { return player; } public Texture2D GetBuildImage(string name) { for (int i = 0; i < buildings.Length; i++) { Building building = buildings[i].GetComponent(); if (building && building.name == name) return building.buildImage; } for (int i = 0; i < units.Length; i++) { Unit unit = units[i].GetComponent(); if (unit && unit.name == name) return unit.buildImage; } return null; } } Enums.cs
namespace RTS { public enum CursorState { Select, Move, Attack, PanLeft, PanRight, PanUp, PanDown, Harvest, RallyPoint } public enum ResourceType { Money, Power, Ore, Unknown} } TeamColor.cs
using UnityEngine; using System.Collections; public class TeamColor : MonoBehaviour { //wrapper class to allow a user-defined part of a model to recieve a team color in the material of that part } Menu.cs
using UnityEngine; using System.Collections; using System.Collections.Generic; using RTS; public class Menu : MonoBehaviour { public GUISkin mySkin; public Texture2D header; public AudioClip clickSound; public float clickVolume = 1.0f; protected string[] buttons; private AudioElement audioElement; protected virtual void Start() { SetButtons(); UnityEngine.Cursor.visible = true; if (clickVolume < 0.0f) clickVolume = 0.0f; if (clickVolume > 1.0f) clickVolume = 1.0f; List sounds = new List(); List volumes = new List(); sounds.Add(clickSound); volumes.Add(clickVolume); audioElement = new AudioElement(sounds, volumes, "Menu", null); } protected virtual void OnGUI() { DrawMenu(); } protected virtual void DrawMenu() { //default implementation for a menu consisting of a vertical list of buttons GUI.skin = mySkin; float menuHeight = GetMenuHeight(); float groupLeft = Screen.width / 2 - ResourceManager.MenuWidth / 2; float groupTop = Screen.height / 2 - menuHeight / 2; GUI.BeginGroup(new Rect(groupLeft, groupTop, ResourceManager.MenuWidth, menuHeight)); //background box GUI.Box(new Rect(0, 0, ResourceManager.MenuWidth, menuHeight), ""); //header image GUI.DrawTexture(new Rect(ResourceManager.Padding, ResourceManager.Padding, ResourceManager.HeaderWidth, ResourceManager.HeaderHeight), header); //menu buttons if (buttons != null) { float leftPos = ResourceManager.MenuWidth / 2 - ResourceManager.ButtonWidth / 2; float topPos = 2 * ResourceManager.Padding + header.height; for (int i = 0; i < buttons.Length; i++) { if (i > 0) topPos += ResourceManager.ButtonHeight + ResourceManager.Padding; if (GUI.Button(new Rect(leftPos, topPos, ResourceManager.ButtonWidth, ResourceManager.ButtonHeight), buttons[i])) { HandleButton(buttons[i]); } } } GUI.EndGroup(); } protected virtual void SetButtons() { //a child class needs to set this for buttons to appear } protected virtual void HandleButton(string text) { //a child class needs to set this to handle button clicks if (audioElement != null) audioElement.Play(clickSound); } protected virtual float GetMenuHeight() { float buttonHeight = 0; if (buttons != null) buttonHeight = buttons.Length * ResourceManager.ButtonHeight; float paddingHeight = 2 * ResourceManager.Padding; if (buttons != null) paddingHeight += buttons.Length * ResourceManager.Padding; return ResourceManager.HeaderHeight + buttonHeight + paddingHeight; } protected void ExitGame() { Application.Quit(); } } MainMenu.cs
using UnityEngine; using System.Collections; using RTS; public class MainMenu : Menu { protected override void SetButtons() { buttons = new string[] { "New Game", "Quit Game" }; } protected override void HandleButton(string text) { base.HandleButton(text); switch (text) { case "New Game": NewGame(); break; case "Quit Game": ExitGame(); break; default: break; } } private void NewGame() { ResourceManager.MenuOpen = false; Application.LoadLevel("Map"); //makes sure that the loaded level runs at normal speed Time.timeScale = 1.0f; } } PauseMenu.cs
using UnityEngine; using RTS; public class PauseMenu : Menu { private Player player; protected override void Start() { base.Start(); player = transform.root.GetComponent(); } void Update() { if (Input.GetKeyDown(KeyCode.Escape)) Resume(); } protected override void SetButtons() { buttons = new string[] { "Resume", "Exit Game" }; } protected override void HandleButton(string text) { base.HandleButton(text); switch (text) { case "Resume": Resume(); break; case "Exit Game": ReturnToMainMenu(); break; default: break; } } private void Resume() { Time.timeScale = 1.0f; GetComponent().enabled = false; if (player) player.GetComponent().enabled = true; Cursor.visible = false; ResourceManager.MenuOpen = false; } private void ReturnToMainMenu() { Application.LoadLevel("MainMenu"); Cursor.visible = true; } } ResultsScreen.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class ResultsScreen : MonoBehaviour { public GUISkin skin; public AudioClip clickSound; public float clickVolume = 1.0f; private AudioElement audioElement; private Player winner; private VictoryCondition metVictoryCondition; void Start() { List sounds = new List(); List volumes = new List(); sounds.Add(clickSound); volumes.Add(clickVolume); audioElement = new AudioElement(sounds, volumes, "ResultsScreen", null); } void OnGUI() { GUI.skin = skin; GUI.BeginGroup(new Rect(0, 0, Screen.width, Screen.height)); //display float padding = ResourceManager.Padding; float itemHeight = ResourceManager.ButtonHeight; float buttonWidth = ResourceManager.ButtonWidth; float leftPos = padding; float topPos = padding; GUI.Box(new Rect(0, 0, Screen.width, Screen.height), ""); string message = "Game Over"; if (winner) message = "Congratulations " + winner.username + "! You have won by " + metVictoryCondition.GetDescription(); GUI.Label(new Rect(leftPos, topPos, Screen.width - 2 * padding, itemHeight), message); leftPos = Screen.width / 2 - padding / 2 - buttonWidth; topPos += itemHeight + padding; if (GUI.Button(new Rect(leftPos, topPos, buttonWidth, itemHeight), "New Game")) { PlayClick(); //makes sure that the loaded level runs at normal speed Application.LoadLevel("Map"); Time.timeScale = 1.0f; ResourceManager.MenuOpen = false; } leftPos += padding + buttonWidth; if (GUI.Button(new Rect(leftPos, topPos, buttonWidth, itemHeight), "Main Menu")) { Application.LoadLevel("MainMenu"); UnityEngine.Cursor.visible = true; } GUI.EndGroup(); } private void PlayClick() { if (audioElement != null) audioElement.Play(clickSound); } public void SetMetVictoryCondition(VictoryCondition victoryCondition) { if (!victoryCondition) return; metVictoryCondition = victoryCondition; winner = metVictoryCondition.GetWinner(); } } Unit.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class Unit : WorldObject { public float moveSpeed, rotateSpeed; public AudioClip driveSound, moveSound; public float driveVolume = 0.5f, moveVolume = 1.0f; protected bool moving, rotating; private Vector3 destination; private Quaternion targetRotation; private GameObject destinationTarget; /*** Game Engine methods, all can be overridden by subclass ***/ protected override void Awake() { base.Awake(); } protected override void Start() { base.Start(); } protected override void Update() { base.Update(); if (rotating) TurnToTarget(); else if (moving) MakeMove(); } protected override void OnGUI() { base.OnGUI(); } /* Public Methods */ public virtual void SetBuilding(Building building) { //specific initialization for a unit can be specified here } public override void SetHoverState(GameObject hoverObject) { base.SetHoverState(hoverObject); //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected) { bool moveHover = false; if (hoverObject.name == "Ground") { moveHover = true; } else { Resource resource = hoverObject.transform.parent.GetComponent(); if (resource && resource.isEmpty()) moveHover = true; } if (moveHover) player.hud.SetCursorState(CursorState.Move); } } public override void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) { base.MouseClick(hitObject, hitPoint, controller); //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected) { bool clickedOnEmptyResource = false; if (hitObject.transform.parent) { Resource resource = hitObject.transform.parent.GetComponent(); if (resource && resource.isEmpty()) clickedOnEmptyResource = true; } if ((hitObject.name == "Ground" || clickedOnEmptyResource) && hitPoint != ResourceManager.InvalidPosition) { float x = hitPoint.x; //makes sure that the unit stays on top of the surface it is on float y = hitPoint.y + player.SelectedObject.transform.position.y; float z = hitPoint.z; Vector3 destination = new Vector3(x, y, z); StartMove(destination); } } } public virtual void StartMove(Vector3 destination) { if (audioElement != null) audioElement.Play(moveSound); this.destination = destination; destinationTarget = null; targetRotation = Quaternion.LookRotation(destination - transform.position); rotating = true; moving = false; } public void StartMove(Vector3 destination, GameObject destinationTarget) { StartMove(destination); this.destinationTarget = destinationTarget; } /* Private worker methods */ private void TurnToTarget() { transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotateSpeed); CalculateBounds(); //sometimes it gets stuck exactly 180 degrees out in the calculation and does nothing, this check fixes that Quaternion inverseTargetRotation = new Quaternion(-targetRotation.x, -targetRotation.y, -targetRotation.z, -targetRotation.w); if (transform.rotation == targetRotation || transform.rotation == inverseTargetRotation) { rotating = false; moving = true; if (destinationTarget) CalculateTargetDestination(); if (audioElement != null) audioElement.Play(driveSound); } } private void CalculateTargetDestination() { //calculate number of unit vectors from unit centre to unit edge of bounds Vector3 originalExtents = selectionBounds.extents; Vector3 normalExtents = originalExtents; normalExtents.Normalize(); float numberOfExtents = originalExtents.x / normalExtents.x; int unitShift = Mathf.FloorToInt(numberOfExtents); //calculate number of unit vectors from target centre to target edge of bounds WorldObject worldObject = destinationTarget.GetComponent(); if (worldObject) originalExtents = worldObject.GetSelectionBounds().extents; else originalExtents = new Vector3(0.0f, 0.0f, 0.0f); normalExtents = originalExtents; normalExtents.Normalize(); numberOfExtents = originalExtents.x / normalExtents.x; int targetShift = Mathf.FloorToInt(numberOfExtents); //calculate number of unit vectors between unit centre and destination centre with bounds just touching int shiftAmount = targetShift + unitShift; //calculate direction unit needs to travel to reach destination in straight line and normalize to unit vector Vector3 origin = transform.position; Vector3 direction = new Vector3(destination.x - origin.x, 0.0f, destination.z - origin.z); direction.Normalize(); //destination = center of destination - number of unit vectors calculated above //this should give us a destination where the unit will not quite collide with the target //giving the illusion of moving to the edge of the target and then stopping for (int i = 0; i < shiftAmount; i++) destination -= direction; destination.y = destinationTarget.transform.position.y; } private void MakeMove() { transform.position = Vector3.MoveTowards(transform.position, destination, Time.deltaTime * moveSpeed); if (transform.position == destination) { moving = false; movingIntoPosition = false; if (audioElement != null) audioElement.Stop(driveSound); } CalculateBounds(); } protected override void InitialiseAudio() { base.InitialiseAudio(); List sounds = new List(); List volumes = new List(); if (driveVolume < 0.0f) driveVolume = 0.0f; if (driveVolume > 1.0f) driveVolume = 1.0f; volumes.Add(driveVolume); sounds.Add(driveSound); if (moveVolume < 0.0f) moveVolume = 0.0f; if (moveVolume > 1.0f) moveVolume = 1.0f; sounds.Add(moveSound); volumes.Add(moveVolume); audioElement.Add(sounds, volumes); } } Units.cs
using UnityEngine; using System.Collections; public class Units : MonoBehaviour { //wrapper class for unit listing for a player } Tank.cs
using UnityEngine; using RTS; public class Tank : Unit { private Quaternion aimRotation; protected override void Start() { base.Start(); } protected override void Update() { base.Update(); if (aiming) { transform.rotation = Quaternion.RotateTowards(transform.rotation, aimRotation, weaponAimSpeed); CalculateBounds(); //sometimes it gets stuck exactly 180 degrees out in the calculation and does nothing, this check fixes that Quaternion inverseAimRotation = new Quaternion(-aimRotation.x, -aimRotation.y, -aimRotation.z, -aimRotation.w); if (transform.rotation == aimRotation || transform.rotation == inverseAimRotation) { aiming = false; } } } public override bool CanAttack() { return true; } protected override void UseWeapon() { base.UseWeapon(); Vector3 spawnPoint = transform.position; spawnPoint.x += (2.1f * transform.forward.x); spawnPoint.y += 1.4f; spawnPoint.z += (2.1f * transform.forward.z); GameObject gameObject = (GameObject)Instantiate(ResourceManager.GetWorldObject("TankProjectile"), spawnPoint, transform.rotation); Projectile projectile = gameObject.GetComponentInChildren(); projectile.SetRange(0.9f * weaponRange); projectile.SetTarget(target); } protected override void AimAtTarget() { base.AimAtTarget(); aimRotation = Quaternion.LookRotation(target.transform.position - transform.position); } } TankProjectile.cs
using UnityEngine; using System.Collections; public class TankProjectile : Projectile { } Projectile.cs
using UnityEngine; using System.Collections; public class Projectile : MonoBehaviour { public float velocity = 1; public int damage = 1; private float range = 1; private WorldObject target; void Update() { if (HitSomething()) { InflictDamage(); Destroy(gameObject); } if (range > 0) { float positionChange = Time.deltaTime * velocity; range -= positionChange; transform.position += (positionChange * transform.forward); } else { Destroy(gameObject); } } public void SetRange(float range) { this.range = range; } public void SetTarget(WorldObject target) { this.target = target; } private bool HitSomething() { if (target && target.GetSelectionBounds().Contains(transform.position)) return true; return false; } private void InflictDamage() { if (target) target.TakeDamage(damage); } } TankU.cs
using UnityEngine; using RTS; public class TankU : Unit { private Quaternion aimRotation; protected override void Start() { base.Start(); } protected override void Update() { base.Update(); if (aiming) { transform.rotation = Quaternion.RotateTowards(transform.rotation, aimRotation, weaponAimSpeed); //CalculateBounds(); //sometimes it gets stuck exactly 180 degrees out in the calculation and does nothing, this check fixes that Quaternion inverseAimRotation = new Quaternion(-aimRotation.x, -aimRotation.y, -aimRotation.z, -aimRotation.w); if (transform.rotation == aimRotation || transform.rotation == inverseAimRotation) { aiming = false; } } } public override bool CanAttack() { return true; } protected override void UseWeapon() { base.UseWeapon(); Vector3 spawnPoint = transform.position; spawnPoint.x += (2.1f * transform.forward.x); spawnPoint.y += 1.4f; spawnPoint.z += (2.1f * transform.forward.z); GameObject gameObject = (GameObject)Instantiate(ResourceManager.GetWorldObject("TankProjectileU"), spawnPoint, transform.rotation); ProjectileU projectileU = gameObject.GetComponentInChildren(); projectileU.SetRange(0.9f * weaponRange); projectileU.SetTarget(target); } protected override void AimAtTarget() { base.AimAtTarget(); aimRotation = Quaternion.LookRotation(target.transform.position - transform.position); } } TankProjectileU.cs
using UnityEngine; using System.Collections; public class TankProjectileU : ProjectileU { } ProjectileU.cs
using UnityEngine; using System.Collections; public class ProjectileU : MonoBehaviour { public float velocity = 1; public int damage = 1; private float range = 1; private WorldObject target; void Update() { if (HitSomething()) { InflictDamage(); Destroy(gameObject); } if (range > 0) { float positionChange = Time.deltaTime * velocity; range -= positionChange; transform.position += (positionChange * transform.forward); } else { Destroy(gameObject); } } public void SetRange(float range) { this.range = range; } public void SetTarget(WorldObject target) { this.target = target; } private bool HitSomething() { if (target && target.GetSelectionBounds().Contains(transform.position)) return true; return false; } private void InflictDamage() { if (target) target.TakeDamage(damage); } } Worker.cs
using UnityEngine; using System.Collections.Generic; public class Worker : Unit { public int buildSpeed; public AudioClip finishedJobSound; public float finishedJobVolume = 1.0f; private Building currentProject; private bool building = false; private float amountBuilt = 0.0f; /*** Game Engine methods, all can be overridden by subclass ***/ protected override void Start() { base.Start(); actions = new string[] { "Refinery", "WarFactory" }; } protected override void Update() { base.Update(); if (!moving && !rotating) { if (building && currentProject && currentProject.UnderConstruction()) { amountBuilt += buildSpeed * Time.deltaTime; int amount = Mathf.FloorToInt(amountBuilt); if (amount > 0) { amountBuilt -= amount; currentProject.Construct(amount); if (!currentProject.UnderConstruction()) { building = false; if (audioElement != null) audioElement.Play(finishedJobSound); } } } } } /*** Public Methods ***/ public override void SetBuilding(Building project) { base.SetBuilding(project); currentProject = project; StartMove(currentProject.transform.position, currentProject.gameObject); building = true; } public override void PerformAction(string actionToPerform) { base.PerformAction(actionToPerform); CreateBuilding(actionToPerform); } public override void StartMove(Vector3 destination) { base.StartMove(destination); amountBuilt = 0.0f; building = false; } public override void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) { bool doBase = true; //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected && hitObject && hitObject.name != "Ground") { Building building = hitObject.transform.parent.GetComponent(); if (building) { if (building.UnderConstruction()) { SetBuilding(building); doBase = false; } } } if (doBase) base.MouseClick(hitObject, hitPoint, controller); } /*** Private Methods ***/ private void CreateBuilding(string buildingName) { Vector3 buildPoint = new Vector3(transform.position.x, transform.position.y, transform.position.z + 10); if (player) player.CreateBuilding(buildingName, buildPoint, this, playingArea); } protected override void InitialiseAudio() { base.InitialiseAudio(); if (finishedJobVolume < 0.0f) finishedJobVolume = 0.0f; if (finishedJobVolume > 1.0f) finishedJobVolume = 1.0f; List sounds = new List(); List volumes = new List(); sounds.Add(finishedJobSound); volumes.Add(finishedJobVolume); audioElement.Add(sounds, volumes); } } Harvester.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class Harvester : Unit { public float capacity, collectionAmount, depositAmount; public Building resourceStore; public AudioClip emptyHarvestSound, harvestSound, startHarvestSound; public float emptyHarvestVolume = 0.5f, harvestVolume = 0.5f, startHarvestVolume = 1.0f; private bool harvesting = false, emptying = false; private float currentLoad = 0.0f, currentDeposit = 0.0f; private ResourceType harvestType; private Resource resourceDeposit; /*** Game Engine methods, all can be overridden by subclass ***/ protected override void Start() { base.Start(); harvestType = ResourceType.Unknown; } protected override void Update() { base.Update(); if (!rotating && !moving) { if (harvesting || emptying) { Arms[] arms = GetComponentsInChildren(); foreach (Arms arm in arms) arm.GetComponent().enabled = true; if (harvesting) { Collect(); if (currentLoad >= capacity || resourceDeposit.isEmpty()) { //make sure that we have a whole number to avoid bugs //caused by floating point numbers currentLoad = Mathf.Floor(currentLoad); harvesting = false; emptying = true; foreach (Arms arm in arms) arm.GetComponent().enabled = false; StartMove(resourceStore.transform.position, resourceStore.gameObject); } } else { Deposit(); if (currentLoad <= 0) { emptying = false; foreach (Arms arm in arms) arm.GetComponent().enabled = false; if (!resourceDeposit.isEmpty()) { harvesting = true; StartMove(resourceDeposit.transform.position, resourceDeposit.gameObject); } } } } } } /* Public Methods */ public override void SetBuilding(Building store) { base.SetBuilding(store); resourceStore = store; } public override void SetHoverState(GameObject hoverObject) { base.SetHoverState(hoverObject); //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected) { if (hoverObject.name != "Ground") { Resource resource = hoverObject.transform.parent.GetComponent(); if (resource && !resource.isEmpty()) player.hud.SetCursorState(CursorState.Harvest); } } } public override void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) { base.MouseClick(hitObject, hitPoint, controller); //only handle input if owned by a human player if (player && player.human) { if (hitObject.name != "Ground") { Resource resource = hitObject.transform.parent.GetComponent(); if (resource && !resource.isEmpty()) { //make sure that we select harvester remains selected if (player.SelectedObject) player.SelectedObject.SetSelection(false, playingArea); SetSelection(true, playingArea); player.SelectedObject = this; StartHarvest(resource); } } else StopHarvest(); } } /* Private Methods */ private void StartHarvest(Resource resource) { if (audioElement != null) audioElement.Play(startHarvestSound); resourceDeposit = resource; StartMove(resource.transform.position, resource.gameObject); //we can only collect one resource at a time, other resources are lost if (harvestType == ResourceType.Unknown || harvestType != resource.GetResourceType()) { harvestType = resource.GetResourceType(); currentLoad = 0.0f; } harvesting = true; emptying = false; } private void StopHarvest() { } private void Collect() { if (audioElement != null && Time.timeScale > 0) audioElement.Play(harvestSound); float collect = collectionAmount * Time.deltaTime; //make sure that the harvester cannot collect more than it can carry if (currentLoad + collect > capacity) collect = capacity - currentLoad; resourceDeposit.Remove(collect); currentLoad += collect; } private void Deposit() { if (audioElement != null && Time.timeScale > 0) audioElement.Play(emptyHarvestSound); currentDeposit += depositAmount * Time.deltaTime; int deposit = Mathf.FloorToInt(currentDeposit); if (deposit >= 1) { if (deposit > currentLoad) deposit = Mathf.FloorToInt(currentLoad); currentDeposit -= deposit; currentLoad -= deposit; ResourceType depositType = harvestType; if (harvestType == ResourceType.Ore) depositType = ResourceType.Money; player.AddResource(depositType, deposit); } } protected override void DrawSelectionBox(Rect selectBox) { base.DrawSelectionBox(selectBox); float percentFull = currentLoad / capacity; float maxHeight = selectBox.height - 4; float height = maxHeight * percentFull; float leftPos = selectBox.x + selectBox.width - 7; float topPos = selectBox.y + 2 + (maxHeight - height); float width = 5; Texture2D resourceBar = ResourceManager.GetResourceHealthBar(harvestType); if (resourceBar) GUI.DrawTexture(new Rect(leftPos, topPos, width, height), resourceBar); } protected override void InitialiseAudio() { base.InitialiseAudio(); List sounds = new List(); List volumes = new List(); if (emptyHarvestVolume < 0.0f) emptyHarvestVolume = 0.0f; if (emptyHarvestVolume > 1.0f) emptyHarvestVolume = 1.0f; sounds.Add(emptyHarvestSound); volumes.Add(emptyHarvestVolume); if (harvestVolume < 0.0f) harvestVolume = 0.0f; if (harvestVolume > 1.0f) harvestVolume = 1.0f; sounds.Add(harvestSound); volumes.Add(harvestVolume); if (startHarvestVolume < 0.0f) startHarvestVolume = 0.0f; if (startHarvestVolume > 1.0f) startHarvestVolume = 1.0f; sounds.Add(startHarvestSound); volumes.Add(startHarvestVolume); audioElement.Add(sounds, volumes); } } Arms.cs
using UnityEngine; public class Arms : MonoBehaviour { //wrapper class for the arms of the harvester } Building.cs using UnityEngine; using System.Collections.Generic; using RTS; public class Building : WorldObject { public float maxBuildProgress; public Texture2D rallyPointImage, sellImage; public AudioClip finishedJobSound; public float finishedJobVolume = 1.0f; protected Vector3 spawnPoint, rallyPoint; protected Queue buildQueue; private float currentBuildProgress = 0.0f; private bool needsBuilding = false; /*** Game Engine methods, all can be overridden by subclass ***/ protected override void Awake() { base.Awake(); buildQueue = new Queue(); float spawnX = selectionBounds.center.x + transform.forward.x * selectionBounds.extents.x + transform.forward.x * 10; float spawnZ = selectionBounds.center.z + transform.forward.z + selectionBounds.extents.z + transform.forward.z * 10; spawnPoint = new Vector3(spawnX, 0.0f, spawnZ); rallyPoint = spawnPoint; } protected override void Start() { base.Start(); } protected override void Update() { base.Update(); ProcessBuildQueue(); } protected override void OnGUI() { base.OnGUI(); if (needsBuilding) DrawBuildProgress(); } /*** Internal worker methods that can be accessed by subclass ***/ protected void CreateUnit(string unitName) { GameObject unit = ResourceManager.GetUnit(unitName); Unit unitObject = unit.GetComponent(); if (player && unitObject) player.RemoveResource(ResourceType.Money, unitObject.cost); buildQueue.Enqueue(unitName); } protected void ProcessBuildQueue() { if (buildQueue.Count > 0) { currentBuildProgress += Time.deltaTime * ResourceManager.BuildSpeed; if (currentBuildProgress > maxBuildProgress) { if (player) player.AddUnit(buildQueue.Dequeue(), spawnPoint, rallyPoint, transform.rotation, this); currentBuildProgress = 0.0f; if (audioElement != null) audioElement.Play(finishedJobSound); } } } protected override void InitialiseAudio() { base.InitialiseAudio(); if (finishedJobVolume < 0.0f) finishedJobVolume = 0.0f; if (finishedJobVolume > 1.0f) finishedJobVolume = 1.0f; List sounds = new List(); List volumes = new List(); sounds.Add(finishedJobSound); volumes.Add(finishedJobVolume); audioElement.Add(sounds, volumes); } /*** Public methods ***/ public string[] getBuildQueueValues() { string[] values = new string[buildQueue.Count]; int pos = 0; foreach (string unit in buildQueue) values[pos++] = unit; return values; } public float getBuildPercentage() { return currentBuildProgress / maxBuildProgress; } public override void SetSelection(bool selected, Rect playingArea) { base.SetSelection(selected, playingArea); if (player) { RallyPoint flag = player.GetComponentInChildren(); if (selected) { if (flag && player.human && spawnPoint != ResourceManager.InvalidPosition && rallyPoint != ResourceManager.InvalidPosition) { flag.transform.localPosition = rallyPoint; flag.transform.forward = transform.forward; flag.Enable(); } } else { if (flag && player.human) flag.Disable(); } } } public bool hasSpawnPoint() { return spawnPoint != ResourceManager.InvalidPosition && rallyPoint != ResourceManager.InvalidPosition; } public override void SetHoverState(GameObject hoverObject) { base.SetHoverState(hoverObject); //only handle input if owned by a human player and currently selected if (player && player.human && currentlySelected) { if (hoverObject.name == "Ground") { if (player.hud.GetPreviousCursorState() == CursorState.RallyPoint) player.hud.SetCursorState(CursorState.RallyPoint); } } } public override void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) { base.MouseClick(hitObject, hitPoint, controller); //only handle iput if owned by a human player and currently selected if (player && player.human && currentlySelected) { if (hitObject.name == "Ground") { if ((player.hud.GetCursorState() == CursorState.RallyPoint || player.hud.GetPreviousCursorState() == CursorState.RallyPoint) && hitPoint != ResourceManager.InvalidPosition) { SetRallyPoint(hitPoint); } } } } public override bool IsActive { get { return !needsBuilding; } } public void SetRallyPoint(Vector3 position) { rallyPoint = position; if (player && player.human && currentlySelected) { RallyPoint flag = player.GetComponentInChildren(); if (flag) flag.transform.localPosition = rallyPoint; } } public void Sell() { if (player) player.AddResource(ResourceType.Money, sellValue); if (currentlySelected) SetSelection(false, playingArea); Destroy(this.gameObject); } public void StartConstruction() { CalculateBounds(); needsBuilding = true; hitPoints = 0; SetSpawnPoint(); } public bool UnderConstruction() { return needsBuilding; } public void Construct(int amount) { hitPoints += amount; if (hitPoints >= maxHitPoints) { hitPoints = maxHitPoints; needsBuilding = false; RestoreMaterials(); SetTeamColor(); } } /*** Private Methods ***/ private void DrawBuildProgress() { GUI.skin = ResourceManager.SelectBoxSkin; Rect selectBox = WorkManager.CalculateSelectionBox(selectionBounds, playingArea); //Draw the selection box around the currently selected object, within the bounds of the main draw area GUI.BeginGroup(playingArea); CalculateCurrentHealth(0.5f, 0.99f); DrawHealthBar(selectBox, "Building ..."); GUI.EndGroup(); } private void SetSpawnPoint() { float spawnX = selectionBounds.center.x + transform.forward.x * selectionBounds.extents.x + transform.forward.x * 10; float spawnZ = selectionBounds.center.z + transform.forward.z * selectionBounds.extents.z + transform.forward.z * 10; spawnPoint = new Vector3(spawnX, 0.0f, spawnZ); rallyPoint = spawnPoint; } } Buildings.cs
using UnityEngine; using System.Collections; public class Buildings : MonoBehaviour { } WarFactory.cs
using UnityEngine; using System.Collections; public class WarFactory : Building { protected override void Start() { base.Start(); actions = new string[] { "Tank", "Worker", "TankU" }; } public override void PerformAction(string actionToPerform) { base.PerformAction(actionToPerform); CreateUnit(actionToPerform); } } Refinery.cs
using UnityEngine; using System.Collections; public class Refinery : Building { //Public variables //Variables accessible by subclass //Private variables //Game Engine methods protected override void Start() { base.Start(); actions = new string[] { "Harvester" }; } public override void PerformAction(string actionToPerform) { base.PerformAction(actionToPerform); CreateUnit(actionToPerform); } //Public methods //Internal methods //Private methods } RallyPoint.cs
using UnityEngine; using System.Collections; public class RallyPoint : MonoBehaviour { public void Enable() { Renderer[] renderers = GetComponentsInChildren(); foreach (Renderer renderer in renderers) renderer.enabled = true; } public void Disable() { Renderer[] renderers = GetComponentsInChildren(); foreach (Renderer renderer in renderers) renderer.enabled = false; } } Resource.cs
using UnityEngine; using RTS; public class Resource : WorldObject { //Public variables public float capacity; //Variables accessible by subclass protected float amountLeft; protected ResourceType resourceType; /*** Game Engine methods, all can be overridden by subclass ***/ protected override void Start() { base.Start(); amountLeft = capacity; resourceType = ResourceType.Unknown; } /*** Public methods ***/ public void Remove(float amount) { amountLeft -= amount; if (amountLeft < 0) amountLeft = 0; } public bool isEmpty() { return amountLeft <= 0; } public ResourceType GetResourceType() { return resourceType; } protected override void CalculateCurrentHealth(float lowSplit, float highSplit) { healthPercentage = amountLeft / capacity; healthStyle.normal.background = ResourceManager.GetResourceHealthBar(resourceType); } } OreDeposit.cs
using UnityEngine; using System.Collections.Generic; using RTS; public class OreDeposit : Resource { //Public variables //Variables accessible by subclass //Private variables private int numBlocks; //Game Engine methods protected override void Start() { base.Start(); numBlocks = GetComponentsInChildren().Length; resourceType = ResourceType.Ore; } protected override void Update() { base.Update(); float percentLeft = (float)amountLeft / (float)capacity; if (percentLeft < 0) percentLeft = 0; int numBlocksToShow = (int)(percentLeft * numBlocks); Ore[] blocks = GetComponentsInChildren(); if (numBlocksToShow >= 0 && numBlocksToShow < blocks.Length) { Ore[] sortedBlocks = new Ore[blocks.Length]; //sort the list from highest to lowest foreach (Ore ore in blocks) { sortedBlocks[blocks.Length - int.Parse(ore.name)] = ore; } for (int i = numBlocksToShow; i < sortedBlocks.Length; i++) { sortedBlocks[i].GetComponent().enabled = false; } CalculateBounds(); } } //Public methods //Internal methods //Private methods } Ore.cs
using UnityEngine; public class Ore : MonoBehaviour { //wrapper class for getting access to a part of an ore deposit } AudioElement.cs
using UnityEngine; using System.Collections; using System.Collections.Generic; public class AudioElement { private GameObject element; private Dictionary<AudioClip, GameObject> soundObjects = new Dictionary<AudioClip, GameObject>(); public AudioElement(List sounds, List volumes, string id, Transform parentTransform) { if (sounds == null || sounds.Count == 0 || volumes == null || volumes.Count == 0 || sounds.Count != volumes.Count) return; element = new GameObject("AudioElement_" + id); if (parentTransform) element.transform.parent = parentTransform; else { //attach it to the game object list (since we know there should be one present) //do so to keep the inspector cleaner - this saves making a sounds object GameObjectList list = MonoBehaviour.FindObjectOfType(typeof(GameObjectList)) as GameObjectList; if (list) element.transform.parent = list.transform; } Add(sounds, volumes); } public void Add(List sounds, List volumes) { for (int i = 0; i < sounds.Count; i++) { AudioClip sound = sounds[i]; if (!sound) continue; GameObject temp = new GameObject(sound.name); temp.AddComponent(typeof(AudioSource)); temp.GetComponent().clip = sound; temp.GetComponent().volume = volumes[i]; temp.transform.parent = element.transform; soundObjects.Add(sound, temp); } } public void Play(AudioClip sound) { GameObject temp; if (soundObjects.TryGetValue(sound, out temp)) { if (!temp.GetComponent().isPlaying) temp.GetComponent().Play(); } } public void Pause(AudioClip sound) { GameObject temp; if (soundObjects.TryGetValue(sound, out temp)) { temp.GetComponent().Pause(); } } public void Stop(AudioClip sound) { GameObject temp; if (soundObjects.TryGetValue(sound, out temp)) { temp.GetComponent().Stop(); } } public bool IsPlaying(AudioClip sound) { GameObject temp; if (soundObjects.TryGetValue(sound, out temp)) { return temp.GetComponent().isPlaying; } return false; } } VictoryCondition.cs
using UnityEngine; using System.Collections; public abstract class VictoryCondition : MonoBehaviour { protected Player[] players; public void SetPlayers(Player[] players) { this.players = players; } public Player[] GetPlayers() { return players; } public virtual bool GameFinished() { if (players == null) return true; foreach (Player player in players) { if (PlayerMeetsConditions(player)) return true; } return false; } public Player GetWinner() { if (players == null) return null; foreach (Player player in players) { if (PlayerMeetsConditions(player)) return player; } return null; } public abstract string GetDescription(); public abstract bool PlayerMeetsConditions(Player player); } AccumulateMoney.cs
using UnityEngine; using System.Collections; using RTS; public class AccumulateMoney : VictoryCondition { //Public variables public int amount = 1050; //Variables accessible by subclass //Private variables private ResourceType type = ResourceType.Money; //Game Engine methods //Public methods public override string GetDescription() { return "AccumulateMoney"; } public override bool PlayerMeetsConditions(Player player) { return player && !player.IsDead() && player.GetResourceAmount(type) >= amount; } //Internal methods //Private methods } Conquest.cs
using UnityEngine; using System.Collections; public class Conquest : VictoryCondition { public override string GetDescription() { return "Conquest"; } public override bool GameFinished() { if (players == null) return true; int playersLeft = players.Length; foreach (Player player in players) { if (!PlayerMeetsConditions(player)) playersLeft--; } return playersLeft == 1; } public override bool PlayerMeetsConditions(Player player) { return player && !player.IsDead(); } }