Turn Based Battle System

I've been trying to set up a battle system using Unity but I've hit a bit of a snag: Player Turn script

static var Playerturn: boolean = true;

function Awake(){

Select();

}

function Select(){
print("Make your move!!");
yield WaitForSeconds(2);
Battle_Menu.choice = true;
print("Battle Phase Begins!!");
yield WaitForSeconds(2);
//BattlePhase();
}

static function BattlePhase(){
//RPGPlayer.OnAttack();
RPGEnemy.OnDamage();
yield WaitForSeconds(2);
print("Battle Phase Ends!!");
TurnEnd();
}

static function TurnEnd(){
print("Turn end!");
if(Playerturn == true){
Playerturn = false;
Enemy_Turn.Select();
}
if(Playerturn == false){
Playerturn = true;
}
}

function GameOver(){
//Play audio oneshot
//Destroy player
//WaitforSeconds();
//Load level GameOver
}

function Victory(){
//Play audio oneshot
//Destroy enemy
//WaitforSeconds();
//Load level Victory
}

Menu Script:

static var choice: boolean = false;

function OnGUI () {
if (choice == true){
    // Make a background box
    GUI.Box (Rect (10,10,100,90), "Battle Menu");

    // Make the first button. If it is pressed, Application.Loadlevel (1) will be executed
    if (GUI.Button (Rect (20,40,80,20), "Attack")) {
        //Application.LoadLevel (1);
        RPGPlayer.OnAttack();
        print("The Player Attacks!!");
        Run();
        choice = false;
        }

    // Make the second button.
    if (GUI.Button (Rect (20,70,80,20), "Defend")) {
        //Application.LoadLevel (2);
        RPGPlayer.OnDefense();
        print("The Player blocked!!");
        Run();
        choice = false;
        }
    }
    if (choice == false){
    return;

    }

}

function Run(){
Player_Turn.BattlePhase();

}

Basically, I tried to run it to where the player turns begins and displays an attack or defend button using OnGUI and it sends makes the Player Turn function Battle phase run. However, the code stops progressing once the player chooses attack or defend and it stays put.

Does anyone have a solution?

Edit: Thanks to all the gave an answer. This will greatly benefit the Unity community!

I would use a more state based driven code with events to set up your turn based system, now you seem to be trying to do that, but you are waiting an arbitrary length (2 seconds) before you move onto the next screen. Here's an example of how to have one turn trigger the next. It has 2 built-in events. Each one fires when its respective turn ends. It's written in C# because js has a strange if existent ways of doing events and I don't like how it writes Coroutines (C# is more explicit in the latter).

What you still have to add is the instruction for finding an object, and responding to the events. I haven't fully tested this code either. It might be missing a yield or 2.

So here it is full of comments.

using UnityEngine;
using System.Collections;

public delegate void TurnEnded(TurnInfo tI);

public class StateManager : MonoBehaviour {

    public event TurnEnded enemyTurnEnded;
    public event TurnEnded playerTurnEnded;

    // Use this for initialization
    void Start () {
        StartCoroutine(UpdateState());
        //Immediately start our loop
    }

    // Update is called once per frame
    IEnumerator UpdateState () {
        for(;;) {
        //This is short hand for infinity loop.  Same as while(true).   

            yield return StartCoroutine(PlayerTurn());
            //Start our player loop, and wait for it to finish
            //Before we continue.

            yield return StartCoroutine(EnemyTurn());
            //Do enemy loop, finish, restart loop

        }
    }

    IEnumerator PlayerTurn() {

        Transform target = null;
        //This could be placed in a higher scope for memory purposes.

        bool objectSelected = false;
        //have we selected an object.

        TurnInfo tI = new TurnInfo();
        //create a new turn info tracker.

        yield return StartCoroutine(SelectObject(target));
        //Wait until we find a target before continuing.

        if(target != null) 
            yield return StartCoroutine(Attack(target));
        //Wait until we find a target before continuing.

        Debug.Log("Attacked");

        if(playerTurnEnded != null) 
            playerTurnEnded(tI);

    }

    IEnumerator EnemyTurn() {

        Transform target = null;
        //This could be placed in a higher scope for memory purposes.

        bool objectSelected = false;
        //have we selected an object.

        TurnInfo tI = new TurnInfo();
        //create a new turn info tracker.

        yield return StartCoroutine(SelectObject(target));
        //Wait until we find a target before continuing.

        if(target != null) 
            yield return StartCoroutine(Attack(target));
        //Wait until we find a target before continuing.

        Debug.Log("Attacked1");

        if(enemyTurnEnded != null) 
            playerTurnEnded(tI);

    }

    IEnumerator SelectObject (Transform target) {
        bool objectSelected = false;

        while (!objectSelected) {
            target = SomeMethodForFindingATarget();
            if(target != null) {
                objectSelected = true;
                Debug.Log("object selected");
            }

            yield return null;
        }
    }

    Transform SomeMethodForFindingATarget() {
            return null;
                        //return a transform
    }

    IEnumerator Attack (Transform target) {
        bool attacked = false;

        while(!attacked) {

            //Attack the targeted object.
            target.SendMessage("Attacked", SendMessageOptions.DontRequireReceiver);

            //the last value should always be true.
            //If the attack was successful and the turn will end.
            attacked = true;

            yield return null;
        }   

    }

}

public struct TurnInfo {
//custom Turn info struct.  
//Feel free add or remove any fields.

    private float m_DamageDone;
    private string m_MoveUsed;
    private float m_HealthLeft;             
    private bool m_TurnOver;

    public float DamageDone
    {
        get {
            return m_DamageDone;
        }

        set {
            m_DamageDone = value;
        }
    }
    //How much damage did we do.

    public string MoveUsed
    {
        get {
            return m_MoveUsed;
        }

        set {
            m_MoveUsed = value;
        }
    }
    //What move did we use.

    public float HealthLeft
    {
        get {
            return m_HealthLeft;
        }

        set {
            m_HealthLeft = value;
        }
    }
    //How much health do we have left.

    public bool TurnOver
    {
        get {
            return m_TurnOver;
            //read only
        }
    }
    //Should the turn end?

    //Constructor.
    public TurnInfo(float damage, string moveUsed, float healthLeft, bool turnOver) {   
        m_DamageDone = damage;
        m_MoveUsed = moveUsed;
        m_HealthLeft = healthLeft;
        m_TurnOver = turnOver;
    }
}

I revised my code to be more state driven. Should work generally better now. You can find the old version in the edited page if you need it though.

C# Events

Delegates

Properties

This is a sort of rewrite entirely.

Mainly because I got confused reading the original code, I am not accustomed to JS but the bounty made me want to have a stab at it.

The heart of it is the Start function, which allows each player ("human" and "enemy") to take turns. There is no actual damage implementation, and no real "players" at all, just a string representation. You'd want to plug in your actual objects in the code. This is probably more of a scaffolding. If you set battle = false, the current round ends (a round is a turn each).

var action : String;
var player : String;
var battle = true;
var displayGui = false;

function Start () 
{
    while (battle)
    {
        yield PlayerChoice();
        yield BattlePhase();
        yield TurnEnd();

        yield EnemyChoice();
        yield BattlePhase();
        yield TurnEnd();
    }
}

function PlayerChoice()
{
    print("human makes a decision");
    player = "human";
    displayGui = true;

    while (displayGui)
        yield;        
}

function BattlePhase()
{
    print("battle rages on");
    yield new WaitForSeconds(1);
    print(player + " " + action);
    yield new WaitForSeconds(1);
}

function EnemyChoice()
{
    print("enemy makes a decision");
    yield new WaitForSeconds(1);
    player = "enemy";
    action = "attacks";
}

function TurnEnd()
{
    print("turn ends");
    yield new WaitForSeconds(1);
}

function OnGUI()
{
    if (!displayGui) return;

    GUI.Box (Rect (10,10,100,90), "Battle Menu");

    if (GUI.Button (Rect (20,40,80,20), "Attack"))
    {
        action = "attacks";
        displayGui = false;
    } 

    if (GUI.Button (Rect (20,70,80,20), "Defend"))
    {
        action = "defends";
        displayGui = false;
    }
}

Well here's some advice , not really much of an answer.

I really can not figure out why there is no state controller being used for a turn based system. Turn based is all about states. When is who doing what.

Make a control flow for your game.

Somewhat like magic the gathering: (Somewhat similar , this is not exactly the same)

[Draw]

[Upkeep]

[Pre-Attack Main Phase] (spells and creatures lands > etc)

[Attack Phase] --> Declare attackkers, defenders instant spells etc Interaction with enemy

[Post-Attack Main Phase] (more spells creatures lands etc)

[Discard/CleanUp]

Use an enum a bunch of bools any container to keep track of what object is in what state.

If you have a master controller where there is per player per turn instead of all players in one turn (the 2 most common turn based systems) you need to focus on keeping track of pausing when interacting between two objects, without the master controller you need to force control the order in which the events can take place on it self instead rather than also work on global control flow.

Static functions are great sure, untill you acces it when you shouldn't be accessing it. Anybody can call end turn at any time? That takes a lot of accuracy in the control flow, not to mention

Playing sounds and performing attacks should only be called and automatically be called when the player is performing an action called "Attacking" or otherwise named. My point being that when you have a state called "Attack Phase" you can slide permission for the static attack function call to be set to true, for THIS object even would be an even better specification, as globally accessible functions do not differentiate between anything unless caught. Hence my conclusion to use Player as an object and modify it when a master controller decides it gets to do anything at all, all actions the player can perform should be methods inside the player, all public, all encapsulated except the user input requests marked as public , the rest is not accesible and just does the real work. (Public private , if you just went WTF , you can mimic it here if you want )

If you want to skip keeping track of states, and ignore the permissions set to who can call what when in a turn based game, I will simply tell you , test , test , test again, pray it works.

TurnEnd() will always leave Playerturn as 'true', which will no doubt cause problems along the way.

static function TurnEnd(){
   print("Turn end!");
   if(Playerturn == true){    // Playerturn is initially true, so this is true
      Playerturn = false;     // Playerturn is now false
      Enemy_Turn.Select();
   }
   if(Playerturn == false){   // Playerturn was just made false
      Playerturn = true;      // Playerturn is set back to true
   }
}

You could rewrite it like this:

static function TurnEnd(){
   print("Turn end!");
   if(Playerturn == true){           // if it was the playerturn before TurnEnd()
      Enemy_Turn.Select();           // run Enemy_Turn.Select()
   }
      Playerturn = !Playerturn;      // Playerturn boolean is flipped
}

It might not solve all your problems, but it'll be a start. Let me know how it goes!

I'm pretty sure that you can't use yield in a static function. Try to comment out the yield command in the BattlePhase function and i think your good to go.

static function BattlePhase(){
//RPGPlayer.OnAttack();
RPGEnemy.OnDamage();
//yield WaitForSeconds(2);
print("Battle Phase Ends!!");
TurnEnd();
}

Also i agree with Marowi that you might want to look through your if statements and when you are setting the Playerturn. But fixing this bug would make it a lot easier to debug :)

Let me know how it works.

Excellent thread to a complicated topic. This will help me alot. Thanks guys!

hello, I would like to create an undertale battle system can someone please tell me how