Manuel Graf
Manuel Graf

Reputation: 528

Workaround for Constructors in Unity C#

http://answers.unity3d.com/questions/32413/using-constructors-in-unity-c.html That does not exactly solve the following problem:

I could do it like so

GameObject fireball = (GameObject)GameObject.Instantiate(pfFireball, p.Position, p.Rotation);
FireBall fb = (FireBall)fireball.GetComponent(typeof(FireBall));
fb.agressorId = pId;
fb.weaponLevel = p.Controller.WeaponLevel;
networkView.RPC("ShootClientWeapon", RPCMode.All, (int)w, p.PlayerId);

But what if I wanted to let my weapons handle the logic of their appearance/ Instantiation. For Instance if I have a weapon whose gameobject either spawns directly at the position of every player or just the agressor. Fail. I expected something like...

 fireball fb = new FireBall();
 fb.gameObject = prefabFireball;
 fb.agressorId = pId;
 fb.weaponLevel = p.Controller.WeaponLevel;
 fb.Fire();

Is there a workaround? If I make a class not inheriting Monobehaviour then my update method is gone I guess. But the only thing that I need is to handle Instantiation myself.

Upvotes: 2

Views: 8925

Answers (3)

Géry Arduino
Géry Arduino

Reputation: 490

You could have a FireballBehavior inheriting from MonoBehavior AND a Fireball Object not inheriting from anything.

Your FireballBehavior would take care of spawning and killing Fireballs as well as keeping track of those on scene, on the other hand your Fireball Object should be completely data driven and only hold the data weapon.

Allowing you to send message from the server to the FireballBehavior on any gameObject saying "instanciate that object : Fireball"

Am I clear?

class FireballBehavior : MonoBehavior {

    Fireball fireball;
    public void startFireball(Fireball frb) {
        fireball = frb;
        createFireball(); //instanciation and stuff
    }
    //your functions handling creation update and killing

}

class Fireball {

    GameObject gameObject = prefabFireball;
    Int agressorId = pId;
    Int weaponLevel = p.Controller.WeaponLevel;

}

That way you could just send messages from the network to :

 gameobject.GetComponent<FireballBehavior>().startFireball(frbFromServer);

To sum-up : A generic behavior doing the updates and instanciation according to the data and all the data in a small class or struct handled only by the server and sent from it

The advantage with this approach is that it is interchangeable, and as Fireball is a steady state object you can serialize him or store him in database with not too much effort, with just a few changes you could event change Fireball object in FireballBehavior directly during execution to have different fireball at different times...

This is an idea derivated from one of those videos : (dont remember which one... but both are very good to watch)

To complete the answer you could even have this done a very generic way :

class AnyObjectBehavior : MonoBehavior {

    AnyObject Object1;
    public void startFireball(anyObject frb) {
        Object1 = frb;
        initiateAnyObject (); //instanciation and stuff

    }
    //your functions handling creation update and killing

    private void initiateAnyObject () {

         myObjectList.add(Object1) //that way you do not have to 
                                   // use for loops to edit some objects
         //instanciation stuff
    }

}

class AnyObject {
    //Generic properties
    GameObject gameObject = prefabFireball;

}

class Fireball : AnyObject
{
    //fireball specific properties
    Int agressorId = pId;
    Int weaponLevel = p.Controller.WeaponLevel;

}

That way you could just add new classes for any object type you want to instantiate and always use the same behaviorComponent to start update and kill them, and even keep a generic list of all your objects of anyObject type with

List<anyObject> myObjectList = new List<anyObject>() {fireball, carrot, chicken}

Upvotes: 3

Rottjung
Rottjung

Reputation: 513

You can also Instantiate in your custom serialized class you don't need the FireBallBehaviour. Best way to do it i found is making a custom serialized Player class that has instances of your Weapon class which has an instance of an Ammo class.

the Player class could look something like this:

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

[System.Serializable]
public class PlayerCharacter
{
    //public NetworkPlayer newtWorkPlayer;
    public int playerID;
    public GameObject characterObject;
    public string characterName;
    public float walkSpeed;
    public int health;
    public Vector3 spawnPosition;

    public List<Weapon> weapons = new List<Weapon>();
    public Weapon equipedWeapon;

    public PlayerCharacter()
    {
        playerID = 0;
        characterObject = null;
        characterName = "";
        walkSpeed = 0;
        health = 0;
    }

     public PlayerCharacter(/*NetworkPlayer nP,*/ int pID, GameObject cO, string cN, float wS, int h, Vector3 sP)
    {
         //newtWorkPlayer = nP;
         playerID = pID;
         characterObject = Network.Instantiate(cO, sP, Quaternion.identity, 0)as GameObject;//GameObject.Instantiate(cO,sP,Quaternion.identity)as GameObject;
        characterName = cN;
        walkSpeed = wS;
        health = h;
        spawnPosition = sP;
    }

    public void TakeDamage (int takeDamage)
    {
        health -= takeDamage;
    }

    public void Movement (Vector3 target)
    {
        characterObject.transform.position += target;
    }
}

Your Weapon class:

using UnityEngine;
using System.Collections;

[System.Serializable]
public class Weapon 
{
    public GameObject weaponObject;
    public WeaponType typeOfWeapon;
    public Ammo ammo;

    public Weapon (GameObject wO, WeaponType tOW, Ammo a)
    {
        weaponObject = wO;
        typeOfWeapon = tOW;
        ammo = a;
    }

    public void UseWeapon()
    {
        switch(typeOfWeapon)
        {
        case WeaponType.FireBall:
            //some weapon code here
            break;
        case WeaponType.RidiculousHugeGun:
            //some weapon code here
            break;
        case WeaponType.MegaAwesomeMagicPower:
            //some weapon code here
            break;
        case WeaponType.Knife:
            //some weapon code here
            break;
        }
    }
}
public enum WeaponType
{
    FireBall,
    RidiculousHugeGun,
    MegaAwesomeMagicPower,
    Knife
}

Your Ammo class:

using UnityEngine;
using System.Collections;

[System.Serializable]
public class Ammo 
{
    public GameObject ammoObject;
    public int damage;
    public float moveSpeed;

    public Ammo(GameObject aO, int d, float mS)
    {
        ammoObject = GameObject.Instantiate(aO)as GameObject;
        damage = d;
        moveSpeed = mS;
    }
    public IEnumerator Movement (Vector3 target)
    {
        while(ammoObject != null)
        {
            ammoObject.transform.position = ammoObject.transform.forward+target*moveSpeed*Time.deltaTime;

            yield return null;
        }

    }
}
public enum AmmoType
{
    FireBallBall,
    RidiculousHugeBullet,
    MegaAwesomeMagicPowerEffect,
    None
}

Then you would have a PlayerController thats Monobehaviour to handle all the Input and Collision detection: I only give a movement example here because its getting to much code here already, but i guess you can imagine how you would do the same with the weapons. Since you're weapon is accessible through the player instance as player.currentWeapon or the ammo through player.currentWeapon.ammo

using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {

    public PlayerCharacter player;

    void Update () 
    {
        if(Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
        {
            player.Movement(new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"))*player.walkSpeed*Time.deltaTime);
        }
    }
}

In multiplayer the player gets his instance from the GameServer:

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

public class GameServer : MonoBehaviour 
{
    public List<PlayerCharacter> players = new List<PlayerCharacter>();
    private int playerCount = 0;

    void OnPlayerConnected(NetworkPlayer player) 
    {
        networkView.RPC("CreatePlayer", player);
        Debug.Log("Player " + playerCount + " connected from " + player.ipAddress + ":" + player.port);
    }

    [RPC]
    void CreatePlayer()
    {
        playerCount++;
        PlayerCharacter playerChar = new PlayerCharacter(/*player,*/ playerCount, (GameObject)Resources.Load("Player"), "Player"+playerCount, 5, 100, Vector3.zero);
        playerChar.characterObject.AddComponent<PlayerController>().player = playerChar;
        players.Add(playerChar);
    }
}

one last thing @ Géry Arduino : I don't see how your example is generic at all? It's not typesafe anywhere, since you are committing the actual data type. for reference about generics read this

Upvotes: -1

Alex
Alex

Reputation: 1109

A few ideas :

  1. You can have a fake update loop by using delegates on a dummy gameobject that simply invokes the delegate in it's own update loop.

  2. Lack of constructors can be considered an inconvenience that is easily solved with a generic extension method on GameObject. You can for instance have a .Create<T>(params ...) that will essentially hide all the ugliness and do the instantiation and initialization for you.

I use a similar approach and all my weapons are created from 'drop sheets' that are completely random and so on.

Upvotes: 1

Related Questions