Tile updates and a more in-depth look at code

I’m going to be honest, I really should be doing this more often. Basically, this post will show what has been done up until now. I’ll go over the tiles and their purpose as well as code sources. I’ll go into more detail of each section in later posts.

Source Tile

The source tile is the life-blood of this game: it sends out ‘power’ by pulsing at a set amount (0.5 seconds while debugging; 1 second most likely in the real product). Any tiles that can receive power and are turned on will receive power and pass on to other tiles.

Each source pulse has a unique signature (as unique as a 5 digit number can give) so that each tile can only receive power once from each signature. Basically, if a level becomes really large, I wanted to make sure that activatable/goal tiles don’t become self-powered without use of the source tile.

Source.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SourceTile : MonoBehaviour {

    public float pulseTimer;
    public float pulseInterval;

    public List neighbors;
    private List randomsUsed;

    private float sphereRadius = 2.1f;

    void Start () {
        pulseTimer = 0.0f;
        randomsUsed = new List();
        FindNeighbors();
    }
	
    void Update () {
        if (pulseTimer >= pulseInterval) {
            pulseTimer = 0.0f;
            Debug.Log("Pulse! @ " + Time.time);
            GivePower();
        } else {
            pulseTimer += Time.deltaTime;
        }
    }

    void FindNeighbors () {
        neighbors = new List();

        Collider[] neighborsHit = Physics.OverlapSphere(transform.position, sphereRadius);

        foreach (Collider co in neighborsHit) {
            if ((co.CompareTag("Activatable") ||
                co.CompareTag("GoalTile") ||
                co.CompareTag("SourceTile")) &&
                co.transform != transform) {
                neighbors.Add(co.gameObject);
            }
        }
    }

    void GivePower () {
        int signature = GetNewRandom(0, 100000);

        foreach (GameObject neighbor in neighbors) {
            Powerable p = neighbor.GetComponent();
            p.GivePower(signature);
        }
    }

    int GetNewRandom(int min, int max) {
        
        // Create random, check list, if exists, choose new, else return int
        int result = Random.Range(min, max);

        if (randomsUsed.Count == 0) {
            Debug.Log("Random = " + result);
            randomsUsed.Add(result);
            return result;
        }

        while (!randomsUsed.Contains(result)) {
            Debug.Log("Random = " + result);
            randomsUsed.Add(result);
        }

        return result;
    }

    // Debug
    void OnDrawGizmosSelected() {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, sphereRadius);
    }
}

Activatable Tiles

Activatable tiles have been split into two parts: ActivatableTile and Powerable.

ActivatableTile

There are a few things that ActivatableTile handles: Material changes, audio, and collision with the player. There are 3 states for Activatables: off, unpowered, and powered.

Material Changes

All the materials are set using the Inspector. The two scenarios that cause a tile to change material are collision and power state changes. However, technically, both scenarios are routed through the Powerable script to allow a single point of change (“Change it once, change it everywhere” mantra).

Audio

The logic that handles tile audio is similar to the material change. If a material is changed, a sound effect will play. There are four sounds that can happen instead of the three material changes (off -> unpowered; unpowered -> powered; powered -> unpowered; and ANY -> off). I plan on using some kind of audio manager later in development, but I like to make things work first.

Collision with Player

Collision with the player will cause a state change in power, toggling between off and unpowered. There is also a timer associated with the collision to make sure the player doesn’t activate the OnCollisionEnter function more than once within that period.

ActivatableTile.cs


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ActivatableTile : MonoBehaviour {

	public Material unactivatedMaterial;
	public Material activatedUnpoweredMaterial;
	public Material activatedPoweredMaterial;

	public AudioClip offClip;
	public AudioClip offToUnpoweredClip;
	public AudioClip unpoweredToPoweredClip;
	public AudioClip poweredToUnpoweredClip;

	public Powerable powerableScript;
	public float collisionInterval;
	public float collisionTimer;
	public bool canCollide;

	void Start () {
		powerableScript = GetComponent ();

		canCollide = true;
		collisionTimer = 0.0f;
	}

	void Update () {
		if (!canCollide) {
			if (collisionTimer > collisionInterval) {
				canCollide = true;
				collisionTimer = 0.0f;
			} else {
				collisionTimer += Time.deltaTime;
			}
		}

		CheckState();
	}

	void OnCollisionEnter (Collision other) {
		if (!other.gameObject.CompareTag ("Player") || !canCollide)
			return;

		//powerableScript.state = powerableScript.state == PowerState.OFF ? PowerState.UNPOWERED : PowerState.OFF;
		if (powerableScript.state == PowerState.OFF) {
			powerableScript.ChangeState(PowerState.UNPOWERED);
		} else {
			powerableScript.ChangeState(PowerState.OFF);
		}

		canCollide = false;

		CheckState();

		Debug.Log(transform.name + " collided with " + other.transform.name + " at time " + Time.time);
	}

	void CheckState () {
		if (powerableScript.hasStateChanged) {
			powerableScript.hasStateChanged = false;

			PowerState currentState = powerableScript.state;
			PowerState lastState = powerableScript.lastState;

			// Material switching
			switch (currentState) {
			case PowerState.OFF:
				renderer.material = unactivatedMaterial;
				//audio.PlayOneShot (offAudioClip);
				break;
			case PowerState.POWERED:
				renderer.material = activatedPoweredMaterial;
				//audio.PlayOneShot (poweredAudioClip);
				break;
			case PowerState.UNPOWERED:
				renderer.material = activatedUnpoweredMaterial;
				//audio.PlayOneShot (unpoweredAudioClip);
				break;
			}

			// Play audio based on last state and current state
			if (lastState == PowerState.OFF && currentState == PowerState.UNPOWERED) {
				audio.PlayOneShot(offToUnpoweredClip);
			} else if (lastState == PowerState.UNPOWERED && currentState == PowerState.POWERED) {
				audio.PlayOneShot(unpoweredToPoweredClip);
			} else if (lastState == PowerState.POWERED && currentState == PowerState.UNPOWERED) {
				audio.PlayOneShot(poweredToUnpoweredClip);
			} else if (currentState == PowerState.OFF) {
				audio.PlayOneShot(offClip);
			}
		}
	}
}

Powerable

I split this class out from the functionality of the activatable tiles so that I could use it for other tiles later, and also to keep my sanity because it was getting to be very intertwined and convoluted code.

Powerable keeps a current state and last state for its power state, as well as a flag for if the current state was changed. This helps other tile classes react to state changes better. There’s also timers, last power signature received information, and the ability to detect neighbor tiles with Powerable scripts attached.

Powerable.cs


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum PowerState {
    OFF,
    UNPOWERED,
    POWERED
}

public class Powerable : MonoBehaviour {

    public PowerState state;
	public PowerState lastState;
	public bool hasStateChanged;

    public float poweredInterval;
    public float poweredTimer;

    public int lastSignatureReceived;
    public List neighbors;

    public float sphereRadius = 2.1f;

    void Start() {
        state = PowerState.OFF;
		lastState = PowerState.OFF;
		hasStateChanged = false;
        poweredTimer = 0.0f;
        lastSignatureReceived = 0;

        FindNeighbors();
    }

    void Update() {
        switch (state) {
            case PowerState.POWERED:
                if (poweredTimer >= poweredInterval) {
                    //state = PowerState.UNPOWERED;
					ChangeState(PowerState.UNPOWERED);
                    poweredTimer = 0.0f;
                    lastSignatureReceived = 0;
                } else {
                    poweredTimer += Time.deltaTime;
                }
                break;
            default:
                poweredTimer = 0.0f;
                lastSignatureReceived = 0;
                break;
        }
    }

	// Called from source or other powerable tiles
    public void GivePower(int signature) {
        if (state == PowerState.OFF) return;

        if (lastSignatureReceived == signature) return;
        else lastSignatureReceived = signature;

        //state = PowerState.POWERED;
		ChangeState(PowerState.POWERED);
        poweredTimer = 0.0f;

        foreach (GameObject neighbor in neighbors) {
            if (neighbor.CompareTag("GoalTile")) {
                GoalTile p = neighbor.GetComponent();
                p.GivePower(signature);
            } else if (neighbor.CompareTag("Activatable")) {
                Powerable p = neighbor.GetComponent();
                p.GivePower(signature);
            }
        }
    }

	// Used to save the current state and flag as changed
	public void ChangeState (PowerState newState) {
		lastState = state;
		state = newState;
		hasStateChanged = true;
	}

    void FindNeighbors() {
        neighbors = new List();

        Collider[] neighborsHit = Physics.OverlapSphere(transform.position, sphereRadius);

        foreach (Collider co in neighborsHit) {
            if ((co.CompareTag("Activatable") || co.CompareTag("GoalTile")) && co.transform != transform) {
                neighbors.Add(co.gameObject);
            }
        }
    }

    // Debug
    void OnDrawGizmosSelected() {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, sphereRadius);
    }
}

Goal Tile

The goal tile is relatively simple. It receives power, adds it to a total, drains at a certain rate, and declares victory when its power goal has been met. Right now, the only visual indicator of power is a light source whose intensity is changed according to current power levels.

GoalTile.cs


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class GoalTile : MonoBehaviour {

    public float currentPower;
    public float powerGoal;
    public float gainRate;
    public float drainRate;

    public GameObject winGUIAnchor;
    private ResetGame winGUIAnchorScript;

    public GameObject lightObject;
    private Light winLight;
    public float maxLightIntensity;
    private float maxLightRatio;

    void Start() {
        currentPower = 0.0f;
        winLight = lightObject.GetComponent();
        winLight.intensity = 0.0f;
        maxLightRatio = 100 / maxLightIntensity;
        winGUIAnchorScript = winGUIAnchor.GetComponent();
    }

    void Update() {
        if (currentPower >= powerGoal) {
            winGUIAnchorScript.hasWon = true;
            return;
        }

        if (currentPower > 0) currentPower -= drainRate * Time.deltaTime;
        winLight.intensity = (currentPower / powerGoal * 100) / maxLightRatio; 
    }

    public void GivePower(int signature) {
        if (currentPower >= powerGoal) return;
        
        currentPower += gainRate;
    }
}

Thanks for sifting through this longer post. Hopefully I can update more often and with more exciting things like pictures and sounds!

Source Block and Goal Tiles

I’ll be introducing two new, important tiles to the game: the source and the goal tiles. This won’t have code, but will be more about the thought process behind the two tiles.

Source Tile

Rolling ball on the source tile

 

This tile pulses with power and sends the signal to activatable tiles, disseminating the signal to other activatable tiles, and finally to the goal tile, if they’re all connected.

Goal Tile

Goal Tile

The goal tile will end the level when it has reached 100% power. This is achieved through connecting it to activatable tiles back to the source tile. Every pulse from one of the goal tile’s neighbors will give it a certain amount of power, while the goal tile will have power burn away at a certain rate (again, set in the editor).

So, that’s it for now. I’ll try to get some code after my self-goal to get the prototype done by Monday

Prototyping a new game

So, I’ve been working on a new prototype: a rolling ball maze where you connect a power source to an end goal. The point will be to connect the two points and power the end goal to 100% to clear the level. Each level will be a small part in a larger map to power a certain object (I haven’t decided what the grand scheme will be, whether powering on objects in a room, or something more thrilling). My current deadline for finishing the prototype is this Monday, June 23. I have most of it worked out besides the power portion, but I already have an idea of how to do that!

Here’s a pretty picture (because I am the best at art).

Rolling ball on the source tile

Rolling ball on the source tile

Leave a comment below so I can possibly incorporate your idea into something.

Hiatus!

So, I’m terrible about updating this, seeing as how it’s been almost a year. Small update on life, I guess: still working at the same IT job, still looking for a game company. I don’t have the capital right now to go full time with game development, so I might start saving up so I can quit for an amount of time without sending my wife and I to the poor-house.

Regardless, I’ve got a new prototype I’m working on.

Vertical Space Shooter

Vertical Space Shooter

What I’ve done so far:

  • Player movement, also restricted to screen space
  • Extensible firing positions placed on ship

Not a ton, but it is just a prototype. I plan on changing up the way the player is clamped to the screen so that enemies can spawn. Right now, it’s physical bumpers keeping it on screen.

Tower Defense #4 – Levels, Experience, and Tower Selection

Whew! It’s been pretty crazy working on this tower defense so far. It’s been almost two weeks since I’ve gotten a post up here. Work has been stupid busy, along with real life and looking for an actual development job. So, here’s the progress update!

  • Towers can now be selected with the mouse. A window will pop up on the left side and show its stats.
  • Towers can change targeting priority through the tower selection window.

Things that I am working on

  • Enemy health scaling.
  • Enemy experience value.
  • Tower leveling scale.
  • Increasable tower stats.

More to come!

Enemy Pathfinding and Deeper Game Mechanics

So, there have been a few things to change since the last iteration.

Enemy Pathfinding

I am using Astar pathfinding (a plugin) to move the Drifters. I’m using a Point graph where I set waypoints using an empty Game Object with cubes. I had to set the distance between getting the next waypoint at 0.1 because the Drifters were cutting corners. The Drifters will look for the waypoint tagged “End Zone”. This modular design will allow me to create any number of levels with predetermined paths.

Cash, Lives, Waves, and Tower Cost

Currently, the player starts out with $100. This value is kind of arbitrary because there is no cost structure to the towers. Right now, Missile towers are $30, Laser are $10, and Flamethrower are $20. The player collects money by destroying enemies. Each enemy is worth $2, so it is really easy to get lots of towers quickly.

The player starts out with a standard 10 lives. There are 5 waves in this current iteration. Not much to talk about with these points.

Other than that, I think that’s all I’ve done  during the week! I’m definitely getting closer to wanting art assets and possibly (gasp) an artist.

Tower Defense #2

I just polished the missile and the laser turret a bit tonight. I ran into a TON of issues with math and how Unity calculates things. Kids, learn your Linear Algebra and your Physics.

Eventually, I figured out the latent rotation of the missiles was caused by a rigidbody z-axis collision (WHAT).

Also, I made a base class TowerProjectile so I could focus on how the Laser moved separate from the Missile.

The green things are missiles with particle trails.

The green things are missiles with particle trails.

I’ll explain how the Laser projectile will lead the enemy after the break.

Continue reading

Tower Defense #1

So, in order to fill out my portfolio, I’m working on making small game prototypes like a side-scrolling space shooter and this tower defense. I got the idea from “#1GAM: How to Succeed at Making One Game a Month”. I’ve been accelerating the process by only having a small brainstorming session to fill out the core motive of a game.

Therefore, here’s the progress being made on the Tower Defense game!

Continue reading