Monday, May 16, 2011

Character creation Part 1 - Unity3d

So the last place we left off, we get to the character select screen and we read in all our characters. Now, I've had the code checked in for nearly a week to get to the character create screen and actually create a character and limit the user to their max characters and limit the names to be unique, including upper/lower case variants of the same name. Today I'm going to go over the changes to get us caught up.

First we will start with the character class in the Unity3d project. We want to allow the user to pick which character to log in with and the easiest way to do that is by the character id. So we need to read it out of the message. We also want to make sure it won't crash if we try to test it before it gets added to the message on the server. The follow code is added to the Character.LoadFromSFSObject()


            if (data.ContainsKey("id"))
            {
                newCharacter.id = data.GetLong("id");
            }
            else
            {
                Debug.Log("SFS Message did not contain character key");
            }

From here we will want to add in a button to go to our character create scene. Before we do that, we want to ensure we received our character list from the server. To do this, we will add a line of code after we add our  CharacterSelectHandler to the list of handlers:



            CharacterSelect.afterMessageRecieved += AfterCharacterSelect;

Now that we have the setup. we need to add the function. Its only job is to tell the GUI to display after we get our data back from the server:

    bool receivedCharacters = false;
    public void AfterCharacterSelect()
    {
        receivedCharacters = true;
    }

Now we can write our OnGUI function and check if we received the characters, if we did, we can display out button.

    void OnGUI()
    {
        if (receivedCharacters)
        {
            foreach (Character character in CharacterSelect.characterList)
            {

            }

            if (GUI.Button(new Rect(100, 165, 100, 25), "New Character") || (Event.current.type == EventType.keyDown && Event.current.character == '\n'))
            {
                UnregisterSFSSceneCallbacks();
                Application.LoadLevel("CharacterCreate");
            }
        }
        if (GUI.Button(new Rect(100, 195, 100, 25), "Back"))
        {
            UnregisterSFSSceneCallbacks();
            smartFox.Disconnect();
            Application.LoadLevel("Lobby");
        }
    }
This code will display our "back" button which will log us out and take us back to the lobby. After we get our character list we enable the new character button which loads our character create scene. Go ahead and create that now by creating a new scene, calling it CharacterCreate, saving it to our scenes folder and then adding it to the build settings so the game knows about the level.

Once the level is added lets go ahead and create a new script called CharacterCreateGUI which, like the CharacterSelectGUI will run our GUI as well as be responsible for sending and receiving messages to and from the server.

using UnityEngine;
using System.Collections;
using Sfs2X.Core;
using Sfs2X.Requests;
using Sfs2X.Entities.Data;
using Sfs2X.Logging;

public class CharacterCreateGUI : ConnectionHandler
{
    bool debugMessages = false;
    bool showErrorDialog = false;
    string characterName = "";
    string sex = "";
    string characterClass = "";

    CharacterCreateHandler CharacterCreate;
    ErrorHandler errorHandler;
    new void Awake()
    {
        base.Awake();
        if (smartFox.IsConnected)
        {
            CharacterCreate = new CharacterCreateHandler();
            errorHandler = new ErrorHandler();
            // Register callback delegate
            smartFox.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
            smartFox.AddEventListener(SFSEvent.LOGOUT, OnLogout);

            smartFox.AddLogListener(LogLevel.DEBUG, OnDebugMessage);

            // We are ready to get the character list
        }
        else
        {
            Application.LoadLevel("Lobby");
        }
    }

    #region Connection Callbacks
    public void OnConnectionLost(BaseEvent evt)
    {
        // Display popup, when user hits ok, return to lobby.
        //loginErrorMessage = "Connection lost / no connection to server";
        Application.LoadLevel("Lobby");
    }

    void OnLogout(BaseEvent evt)
    {
        Application.LoadLevel("Lobby");
    }

    public void OnDebugMessage(BaseEvent evt)
    {
        string message = (string)evt.Params["message"];
        if (debugMessages)
        {
            Debug.Log("**** DEBUG ****" + message);
        }
    }
    #endregion

}

This gets us up and running with our new scene. Next we can add the GUI. I'll be honest, I really didn't take much time on this, its just thrown together to show how we are creating our character:

    void OnGUI()
    {

        if (showErrorDialog)
        {

        }

        GUI.Label(new Rect(120, 116, 100, 100), "Name: ");
        characterName = GUI.TextField(new Rect(200, 116, 200, 20), characterName, 25);

        GUI.Box(new Rect(10, 10, 100, 300), "Classes");

        if (GUI.Button(new Rect(20, 50, 80, 50), "Fighter"))
        {
            characterClass = "Fighter";
        }
        if (GUI.Button(new Rect(20, 110, 80, 50), "Mage"))
        {
            characterClass = "Mage";
        }
        if (GUI.Button(new Rect(20, 170, 80, 50), "Rogue"))
        {
            characterClass = "Rogue";
        }
        if (GUI.Button(new Rect(20, 230, 80, 50), "Cleric"))
        {
            characterClass = "Cleric";
        }

        if (GUI.Button(new Rect(150, 170, 80, 50), "Male"))
        {
            sex = "M";
        }
        if (GUI.Button(new Rect(240, 170, 80, 50), "Female"))
        {
            sex = "F";
        }

        if (GUI.Button(new Rect(200, 265, 100, 25), "Create") || (Event.current.type == EventType.keyDown && Event.current.character == '\n'))
        {
            if (!string.IsNullOrEmpty(characterName) && !string.IsNullOrEmpty(sex) && !string.IsNullOrEmpty(characterClass))
            {
                ISFSObject data = new SFSObject();
                data.PutUtfString("characterName", characterName);
                data.PutUtfString("sex", sex);
                data.PutUtfString("characterClass", characterClass);
                ExtensionRequest request = new ExtensionRequest("createCharacter", data);
                smartFox.Send(request);
            }
        }
    }
We have a piece of the GUI prepared for any errors we receive in the character creation process. This includes duplicate names, invalid names, or if we forget to disable the character create button when they have maxed out their character slots. I chose a simple 4 class, setup. As you can see we wait unless they clicked at least one of the classes, a sex, and chosen a name. You can easily default the starting sex/class and put in the corresponding code to switch models when each button is clicked. From here, when you click the Create button it assembles the packet and ships it off to the server. Now we need to add the code and new class for what happens when get our response.

In the awake function we add the following:

            CharacterCreate = new CharacterCreateHandler();
            errorHandler = new ErrorHandler();

            // Personal message handlers
            handlers.Add("characterCreated", CharacterCreate.HandleMessage);
            handlers.Add("error", errorHandler.HandleMessage);
 
            CharacterCreate.afterMessageRecieved += AfterCharacterCreated;
            errorHandler.afterMessageRecieved += AfterErrorReceived;

This sets us up for the functions and new classes, first the functions AfterCharacterCreated and AfterErrorReceived.

    public void AfterErrorReceived()
    {
        // get the error message and set the error "dialog" to appear with the message.
        showErrorDialog = true;
    }

    public void AfterCharacterCreated()
    {
        UnregisterSFSSceneCallbacks();
        Application.LoadLevel("CharacterSelect");
    }

Next go ahead and create the 2 classes ErrorHandler and CharacterCreateHandler:

class ErrorHandler: IMessageHandler
{
    string errorMessage = "";
    public override void OnHandleMessage(Sfs2X.Entities.Data.ISFSObject data)
    {
        errorMessage = data.GetUtfString("error");
    }

}

public class CharacterCreateHandler : IMessageHandler
{
    public override void OnHandleMessage(Sfs2X.Entities.Data.ISFSObject data)
    {
        Debug.Log("Character created successfully, going back to character select");
    }

}

This concludes everything we need in order to create a character. In Part 2 I will cover receiving the message, creating the character and returning either an error or the success message - CharacterCreated.

No comments: