Getting Transform of Enum Objects - c#

I am confused about how to get the transform component of Enum objects that are bones of a character.
This is easily done in Mecanim by using the GetBoneTransform method, so I am trying to write a function to do this when I am not using a Mecanim skeleton.
The following is my class:
using UnityEngine;
using System.Collections;
public class GetTransform : MonoBehaviour
{
public enum BradBodyBones
{
JtPelvis = 0,
JtSkullA = 1,
JtSkullB = 2
}
void Start()
{
GetEnumTransform(JtPelvis); // This is the error line.
}
Transform GetEnumTransform(BradBodyBones boneName)
{
Transform tmpTransf;
GameObject enumObject = GameObject.Find(boneName.ToString());
tmpTransf = enumObject.transform;
return tmpTransf;
}
}
I know this is wrong, because JtPelvis in the Start() is highlighted red, so can someone please help me understand how I can do this?
In other words, how can I implement a function similar to GetBoneTransform of Unity's HumanBodyBones enumeration that when I use it in my Start() for example, it gives me the transform of JtPelvis?
Basically, GetEnumTransform(JtPelvis); of my Start() should return the transform component of that bone...

Enum is better to be use in case that your enum list does not have a potential to be changed during the application life cycle. I was using enum, for example, in case of odd/even, dice numbers (1 to 6), week (Sunday to Saturday) and so on.
In your case, I was changing the architecture.
Something like:
public class GetTransform : MonoBehaviour
{
Transform GetEnumTransform(BodyBones bodyBone)
{
return bodyBone.GetBodyBoneTransform();
}
}
class BodyBones
{
protected Transform _transform;
public BodyBones(Transform transform)
{
_transform = transform;
}
public Transform GetBodyBoneTransform()
{
return _transform;
}
}
class JtPelvis : BodyBones
{
public JtPelvis (Transform transform): base(transform)
}
class JtSkull : BodyBones
{
public JtSkull (Transform transform): base(transform)
}

What you want is a dictionary. Using an enum also is safer, as you won't be affected by a small typo. You then have to make the appropriate transforms. For instance, your code could look like this:
public class GetTransform : MonoBehaviour
{
public Transform headBone;
public Transform pelvisBone;
private Dictionary<myBonesBody,Transform> mEnumToBone;
void Start() {
mEnumToBone=new Dictionary<myBonesBody,Transform>();
mEnumToBone.Add(myBodyBone.skull,headBone); //Continue as such.
mEnumToBone[myBodyBone.skull]; //This is how you retrieve the value from the dictionary
}
public Transform GetTransform(myBonesBody part) {
return mEnumToBone[part];
}
}

using UnityEngine;
using System.Collections;
public class GetTransform : MonoBehaviour
{
public enum BradBodyBones
{
JtPelvis = 0,
JtSkullA = 1,
JtSkullB = 2
// more here later ...
}
void Start()
{
// Debug.Log(BradBodyBones.JtSkullA); // JtSkullA which is a string!
// Debug.Log((int)(BradBodyBones.JtSkullA)); // 1 which is an integer!
// GameObject Pelvis = GameObject.Find("JtPelvis");
// Debug.Log(Pelvis.transform.position.x);
Debug.Log(GetEnumTransform(BradBodyBones.JtPelvis).position.x);
Debug.Log(GetEnumTransform(BradBodyBones.JtPelvis).position.y);
Debug.Log(GetEnumTransform(BradBodyBones.JtPelvis).position.z);
}
Transform GetEnumTransform(BradBodyBones boneName)
{
Transform tmpTransf;
GameObject enumObject = GameObject.Find(boneName.ToString()); // GameObject.Find(boneName.ToString()).GetComponentInChildren<BradBodyBones>();
tmpTransf = enumObject.transform;
return tmpTransf;
}
}
Many thanks to ehh + Caramiriel + PearsonArtPhoto for helping me resolve this.

Related

How do I pass a variable between scripts in Unity 2d C#

For example, I have a variable "Wisps" that I want to change when the player picks up an object. But I don't know how to do it. I tried to add a WispDisplay object to call the classes, like in Java, but it doesn't seem to work.
public class WispCode : MonoBehaviour
{
WispDisplay wd = new WispDisplay();
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
wd.setWisp(wd.getWisp()+1);
Destroy(gameObject);
}
}
}
public class WispDisplay : MonoBehaviour
{
public int Wisp = 5;
public Text WispText;
void Start()
{
}
void Update()
{
WispText.text = "Wisp: " + Wisp.ToString();
}
public int getWisp()
{
return Wisp;
}
public void setWisp(int newWisp)
{
Wisp = newWisp;
}
}
Easiest (a tiny bit dirty) way is to use a static variable. Downside: you can only have exactly ONE.
Example:
public class MyClass: MonoBehaviour {
public static int wisps;
}
Then, in ANY class, just use this to access it:
MyClass.wisps = 1234;
The more elegant way, working with multiple class instances, is using references.
Example:
public class PlayerClass: MonoBehaviour {
public int wisps = 0;
}
public class MyClass: MonoBehaviour {
public PlayerClass player;
void Update(){
player.wisps += 1;
}
}
Then, you need to drag-drop (aka "assign") the "PlayerClass" Component (attached to the player) to the the Gameobject that should increase the Wisps count. You can duplicate these objects after assigning the reference.
Now, if you actually want to have some sort of collectible, I'd suggest this approach:
You Have a Player "PlayerClass" and some Objects that are collectible, which have Trigger Colliders.
The objects have this code:
public class Example : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
// probably a good idea to check for player tag:
// other.compareTag("Player");
// but you need to create the "Player" Tag and assign it to Player Collider Object.
if(TryGetComponent(out PlayerClass player))
{
player.wisps += 1;
}
}
}

Method is not changing outer variable C#

I want to change a outer variable (var1) inside a method to be able to use a getter to return it's value. But somehow var1 is only changed within the function and not global. How do I change the value of var1 inside of a method globally?
The code is really simple:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PlayerMovement : MonoBehaviour
{
private float var1 = 0f;
void Update()
{
var1 = 5;
print(var1); // output: 5
}
public float GetVar1()
{
print(var1);
return var1; // output: 0
}
}
The update method is called by the gameengine unity itself and the getter is called in another class.
The class that calls the getter is here:
public class bullet : MonoBehaviour
{
public PlayerMovement playerdata;
private float output;
void Update()
{
output = playerdata.GetVar1();
}
}
If you expect output of a bullet instance to be 5, then you need to call Update() on the playerdata instance first:
public class bullet : MonoBehaviour
{
public PlayerMovement playerdata;
private float output;
void Update()
{
playerdata.Update();
output = playerdata.GetVar1();
}
}
I also note that unless bullet is incomplete code, you're not creating an instance of the PlayerMovement class, so you will get a NullReferenceException.

Unity How to GetCompoent<Outline>() & GetCompoent<Shadow>() with a GameObject

public class ABCD : MonoBehaviour
{
[SerializeField]
Outline outline;
[SerializeField]
Shadow shadow;
private void Start()
{
outline = GetComponent<Outline>();
shadow = GetComponent<Shadow>();
}
}
we knew Outline is Implementation Shadow like this.
So When I call
shadow = GetCompoent<Shadow>();
It Possible find out the Outline Compoent , because Ouline is also a Shadow.
My question is How can I get the right Compoent?
And I can't drop the compoent to keep reference.
Because My code exactly like this
//this not a monobehavior
class MyText
{
Shadow shadow;
Outline outline;
public MyText(Transfrom transfrom)
{
outline = transfrom.GetComponent<Outline>();
shadow = transfrom.GetComponent<Shadow>();
}
}
If I create Compoent to keep reference, it will use more cost.
Use GetCompoents Can slove that , Anyone have better solution?
foreach (Shadow s in GetComponents<Shadow>())
{
if (s is Outline)
{
outline = s as Outline;
}
else
{
shadow = s;
}
}
The GetComponent will always return the first available member of asked type.
You need to use Get Components to get more than one. Here's an example from https://unitygem.wordpress.com/getcomponent-in-c/
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour
{
private Force [] scripts = null;
[SerializeField] private int health = 5;
private void Start()
{
this.scripts = this.gameObject.GetComponents<Force>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
for (int i = 0; i < this.scripts.Length; i++)
{
if(this.scripts[i].ReportForce())
{
this.health += 5;
}
}
}
}
}

Wandering AI in Unity

I'm trying to create a wandering AI. I'm using unity standard assets third person AI but the problem is the AI is only moving to a certain point and it can not patrol between these points
Here's the code:
using System;
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (UnityEngine.AI.NavMeshAgent))]
[RequireComponent(typeof (ThirdPersonCharacter))]
public class AICharacterControl : MonoBehaviour
{
// the navmesh agent required for the path finding
public UnityEngine.AI.NavMeshAgent agent { get; private set; }
// the character we are controlling
public ThirdPersonCharacter character { get; private set; }
// target to aim for
public Transform target;
private void Start()
{
// get the components on the object we need (should not be null
// due to require component so no need to check)
agent = GetComponentInChildren<UnityEngine.AI.NavMeshAgent>();
character = GetComponent<ThirdPersonCharacter>();
agent.updateRotation = false;
agent.updatePosition = true;
}
private void Update()
{
if (target != null)
agent.SetDestination(target.position);
if (agent.remainingDistance > agent.stoppingDistance)
character.Move(agent.desiredVelocity, false, false);
else
character.Move(Vector3.zero, false, false);
}
public void SetTarget(Transform target)
{
this.target = target;
}
}
}
How can I modify it to patrol?
To make the AI patrol between two points, you need to define the second point and change the behaviour of the AI to change target when it gets to the first point. Currently, it will simply move at zero velocity once it reaches its target (i.e. stop).
Without modifying your code too much, you can extend it to move between two positions by doing something like this.
using System;
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (UnityEngine.AI.NavMeshAgent))]
[RequireComponent(typeof (ThirdPersonCharacter))]
public class AICharacterControl : MonoBehaviour
{
// the navmesh agent required for the path finding
public UnityEngine.AI.NavMeshAgent agent { get; private set; }
// the character we are controlling
public ThirdPersonCharacter character { get; private set; }
public Transform start;
public Transform end;
private Transform target;
private boolean forward = true;
private void Start()
{
// get the components on the object we need ( should not be null
// due to require component so no need to check )
agent = GetComponentInChildren<UnityEngine.AI.NavMeshAgent>();
character = GetComponent<ThirdPersonCharacter>();
agent.updateRotation = false;
agent.updatePosition = true;
}
private void Update()
{
if (target != null)
agent.SetDestination(target.position);
if (agent.remainingDistance > agent.stoppingDistance)
{
character.Move(agent.desiredVelocity, false, false);
}
else
{
SetTarget(forward ? start : end);
forward = !forward;
}
}
public void SetTarget(Transform target)
{
this.target = target;
}
}
}
As you can see, I've modified the Update() to tell the AI to change target if it gets too close to the current target. There's also an extra Transform definition (start) at the top that need to be set, and a boolean used to store which direction the AI is going.
This code hasn't been tested in Unity so may need some modifications, but it should give you the right idea.

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:
Various Weapon Levels
Server-Client Architecture
Server wants to spawn a weapon or projectile, but BEFORE wants to set which player shot, which level the weapon has etc.
WeaponScript should handle all Effects, like Instantiation themselves
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.
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)
Unite 2013 Scripting behind the scene
Unite 2013 Internal Unity tips and tricks
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}
A few ideas :
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.
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.
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

Categories