Skip to content

Instantly share code, notes, and snippets.

@arun02139 arun02139/Unit.cs
Created Apr 5, 2017

Embed
What would you like to do?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine.Networking;
using UnityEngine.Events;
using UnityEngine.Assertions;
using Random = UnityEngine.Random;
using System.Linq;
public class Unit : NetworkBehaviour
{
//[SyncVar]
public uint playerId;
public int hp;
public int hpMax;
[SyncVar(hook="ChargeHook")]
public float charge;
public bool isCharged;
public int speed;
public int weapon;
public int attackPower;
public int defense;
public int moveRange;
public int jumpRange;
public int level;
public int job;
public GlobalDirection direction;
public UnitSkill skill;
public UnitMovement movement;
public Animator animator;
public Model3D model;
public Point point;
public UnitSMState state;
public List<GameObject> uis = new List<GameObject>();
string _debugString;
string _debugString2;
// individual unit events
public FloatFloatUnityEvent ChargeChanged = new FloatFloatUnityEvent(); // first value is current charge, 2nd value is max charge (i.e. 'speed')
public IntIntIntUnityEvent HpChanged = new IntIntIntUnityEvent(); // first value is old hp, 2nd value is new hp, 3rd value is direction
// static unit events – all units invoke these events and pass along their id, making it easy for other parts
// of the system to observe general unit behaviour (such as checking for game over conditions when any unit
// dies vs looping in a updatee somewhere... QUESTION maybe the complexity doesn't outweigh the benefits though)
public static UIntUnityEvent Charged = new UIntUnityEvent();
public static UIntUnityEvent Died = new UIntUnityEvent();
public override void OnStartAuthority ()
{
base.OnStartAuthority();
// add self to field as soon as we are authorized *and* initialized
StartCoroutine(OnAuthorized());
}
IEnumerator OnAuthorized()
{
while(!_init)
yield return new WaitForEndOfFrame();
RefreshNetUI ();
RefreshParams ();
CmdFwdFieldSetValue (point, netId.Value);
}
bool _init;
void Awake()
{
movement = GetComponent<UnitMovement>();
skill = GetComponent<UnitSkill>();
animator = GetComponent<Animator>();
model = GetComponentInChildren<Model3D>();
}
public override void OnStartClient()
{
// PlayerIdHook(playerId);
ChargeHook(charge);
}
IEnumerator Start()
{
while(netId.Value == 0)
{
_debugString = string.Format("Unit.Start: netId is zero, yielding frame", "");
ClientDebugUI.i.Log (_debugString, GameColor.Magenta);
yield return new WaitForEndOfFrame();
}
while(playerId == 0)
{
// var player = GetComponentInParent<Player> ();
// if (player != null && player.netId.Value > 0) {
// playerId = player.netId.Value;
// continue;
// }
_debugString = string.Format ("Unit.Start: playerId not set, yielding frame", "");
ClientDebugUI.i.Log (_debugString, GameColor.Magenta);
yield return new WaitForEndOfFrame ();
}
while (!Battle.i.players.ContainsKey(playerId))
{ // *can* happen (observed by Arun 2017.03.29 in 1P-Editor)
_debugString = string.Format("Unit.Start: playerId {0} isn't in Battle.players dist, yielding frame", playerId);
ClientDebugUI.i.Log (_debugString, GameColor.Magenta);
yield return new WaitForEndOfFrame ();
}
RefreshNetUI ();
RefreshParams (); // initial params – may need to be reset if we are 'authorized' later (for SyncVars)
// more initialization
point = transform.position.ToPoint();
OverrideAnimationController();
skill.Init ();
// place under player in the hierarchy
transform.SetParent (Battle.i.players [playerId].transform);
// register unit in battle unit dict
Battle.i.units.Add(netId.Value, this);
_debugString = string.Format ("Unit.Start: {0} ready!", netId.Value);
Debug.Log (_debugString);
ClientDebugUI.i.Log (_debugString, GameColor.Cyan);
// add unit id to player unit id list
Battle.i.players [playerId].unitIds.Add (netId.Value);
_init = true;
}
[Command]
void CmdFwdFieldSetValue(Point point, uint id)
{
Field.i.GetTile (point).value = netId.Value;
}
[Command]
public void CmdSetPlayerId(uint id)
{
RpcSetPlayerId (id);
}
[ClientRpc]
public void RpcSetPlayerId(uint id)
{
_debugString = string.Format ("Unit.RpcSetPlayerId: id={0}", id);
Debug.Log (_debugString);
ClientDebugUI.i.Log (_debugString, GameColor.Green);
this.playerId = id;
}
// void PlayerIdHook(uint newPlayerId)
// {
// _debugString = string.Format ("Unit.PlayerIdHook: newPlayerId = {0} ({1})", newPlayerId, hasAuthority);
// Debug.Log (_debugString);
// ClientDebugUI.i.Log (_debugString, GameColor.Green);
// playerId = newPlayerId;
// }
void ChargeHook(float newCharge)
{
newCharge = Mathf.Clamp(newCharge, 0f, (float)speed);
if (charge != newCharge)
{
charge = newCharge;
ChargeChanged.Invoke(charge, (float)speed);
isCharged = charge == (float)speed;
if(isCharged)
{
animator.SetTrigger("Charged");
Charged.Invoke(netId.Value); // static event (for all units!)
}
}
}
void OnDestroy()
{
if(Battle.i != null && Battle.i.units.ContainsKey(netId.Value))
Battle.i.units.Remove(netId.Value);
// clean-up ui
if(UIManager.i != null) {
foreach (var ui in uis) {
UIManager.i.Remove(ui);
}
}
}
public Vector3 WorldPosition(FieldObjectSection topMidBot = FieldObjectSection.None)
{
if(model == null)
{
Debug.LogWarning("UnitBase.WorldPosition: model is null, returning Vector3.zero");
return Vector3.zero;
}
switch(topMidBot)
{
case FieldObjectSection.Top:
return model.worldTop;
case FieldObjectSection.Bottom:
return model.worldBottom;
default:
return model.worldMiddle;
}
}
void SetHP(int newHP, int direction = 0)
{
// _debugString = string.Format ("UnitBase.SetHP: newHP={0}", newHP);
// ClientDebugUI.i.Log (_debugString, GameColor.Black);
// Debug.Log (_debugString);
int oldHP = hp;
hp = newHP < hpMax ? newHP : hpMax; // limit hp to max
if(hp == oldHP) {
_debugString = string.Format("UnitBase.SetHP: new hp same as old hp ({0}), will still invoke HPChanged event", hp);
Debug.LogWarning (_debugString);
}
HpChanged.Invoke(oldHP, hp, direction);
if(hp <= 0)
{
Died.Invoke(netId.Value);
}
}
public void OverrideAnimationController()
{
// use an 'override controller' so we can customize the 'attack state' and clip based on which weapon is equipped
// TEMP, HACK: currently the default human anim controller has 'dagger' attack animation in it's attack state, and
// we override for 'bow' and 'hand' only.
if(weapon == (int)WeaponType.Hand || weapon == (int)WeaponType.Bow)
{
string weaponString = ((WeaponType)weapon).ToString().ToLower();
string s = string.Format("{0}_override", weaponString);
var overrideController = Util.Instantiate<AnimatorOverrideController>(s);
model.SetAnimatorOverrideController(overrideController);
// string debugString = string.Format ("Unit.UpdateAnimationOverrideController: overrideing {0}'s animator with {1}", myName, s);
// ClientDebugUI.i.Log (debugString);
// Debug.Log (debugString);
}
}
void ProcessDeath()
{
//string debugString = string.Format ("UnitBase.ProcessDeath:", "");
//ClientDebugUI.i.Log (debugString);
//Debug.Log (debugString);
hp = 0;
ChargeChanged.Invoke(charge, (float)speed);
// turn off the charge glow (if it was on)
model.HighlightShader(false, HighlightColors.White);
}
public void SetDirection(GlobalDirection newDiection)
{
direction = newDiection;
}
public void TakeDamage(uint damage, uint damagorId, uint direction)
{
//int preHp = this.hp;
int postHp = this.hp - (int)damage;
SetHP (postHp);
// if the unit dies as a result of the action, replace the input callbacks with the death animation
// and move the original callbacks to trigger at the end of the death animation
Action[] callbacks = null;
if (postHp <= 0)
{
// process own death (clears status effects, removes any existing animation logic scripts,
// removes any left over charged actions from the Battle instance, etc.)
ProcessDeath ();
callbacks = new Action[] {
() =>
{
SkillAnimLogic deathAnimLogic = model.gameObject.AddComponent<Death>();
deathAnimLogic.Init();
},
};
}
// flip direction of damage (perspective of receiving unit)
var globalDmgDir = BattleUtil.GetOppositeDirection( (GlobalDirection)direction );
RelativeDirection relativeDir = BattleUtil.GetRelativeDirection(this.direction, globalDmgDir);
// HACK: at the same time as we process the damage, trigger the 'take damage' animation of
// the target (and pass on the callbacks so that they trigger at the end of that animation)
//Debug.Log("b (" + preHp + "," + postHp + ")");
TakeDamage damageAnimLogic = model.gameObject.AddComponent<TakeDamage>();
if (damageAnimLogic != null) {
damageAnimLogic.Init (callbacks, null, relativeDir);
}
}
public void RecoverHp(uint health, uint healorId, uint direction)
{
this.SetHP(hp + Convert.ToInt32(health));
}
[Command]
public void CmdDealDamage(uint damage, uint targetId, uint direction)
{
var targetNetUnit = (Unit)Battle.i.units [targetId];
targetNetUnit.RpcTakeDamage(damage, netId.Value, direction);
}
[ClientRpc]
public void RpcTakeDamage(uint damage, uint damagorId, uint direction)
{
this.TakeDamage(damage, damagorId, direction); // use IUnit interface
}
void RefreshNetUI()
{
NetIdUI netIdUI;
var ids = UIManager.i.FindAll<NetIdUI> ().Where<NetIdUI> (n => n.id == netId.Value).ToList();
if (ids != null && ids.Count > 0) {
Assert.IsTrue (ids.Count == 1, ids.Count.ToString());
netIdUI = ids [0];
} else {
netIdUI = UIManager.i.Add<NetIdUI> ("NetIdUI");
}
// _debugString = string.Format ("Unit.AddRefreshNetUI: {0}", "");
// Debug.Log (_debugString);
// ClientDebugUI.i.Log (_debugString, GameColor.Black);
netIdUI.Init(this, true);
netIdUI.SetTextColor(Battle.i.players[playerId].color);
netIdUI.transform.localScale = Vector3.one * 0.14f;
netIdUI.transform.SetAsLastSibling ();
}
void RefreshParams()
{
UnitParams unitParams = GetComponent<UnitParams> ();
hp = unitParams.hp;
hpMax = unitParams.hpMax;
name = unitParams.myName;
charge = unitParams.charge; // NOTE: this is a SyncVar and so is set again in OnAuthorized
speed = unitParams.speed;
attackPower = unitParams.attackPower;
defense = unitParams.defense;
moveRange = unitParams.moveRange;
jumpRange = unitParams.jumpRange;
level = unitParams.level;
job = (int)unitParams.job;
weapon = (int)unitParams.weapon;
}
// HACK (http://answers.unity3d.com/questions/1170290/unet-indexoutofrangeexception-networkreaderreadbyt.html)
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
base.OnDeserialize(reader, initialState);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.