Day 19:
Added:
More zoms, just cloning one randomly through the maze n times.
Unitychan has health, zom bites remove health.
Made the zom walking animation look a bit better and then somehow made it worse in another way.
I should be defining interfaces for PCs, NPCs, objects and whatnot.
Then they will be able to interact with each other in a standard fashion.
I made some quick interfaces: Interface.cs
/// <summary>
///
/// </summary>
public enum life_status
{
alive,
dead
}
/// <summary>
/// Interface for people / characters in the game.
/// </summary>
public interface person
{
/// <summary>
/// Attack incoming to this character
/// </summary>
/// <param name="power">Power of incoming attack in HP</param>
void attacked(int power, person Attacker);
/// <summary>
/// Get alive/dead status of this person / character.
/// </summary>
/// <returns></returns>
life_status GetLifeStatus();
}
/// <summary>
/// Interface for interactable objects in the game.
/// </summary>
public interface thing
{
//TODO: Put something in here?
}
Thing hasnāt been utilised yet. If I make some objects that PCs and NPCs can interact with.
What is the point of an interface? Every NPC should implement this interface, which means it must have the described set of functions. So for any NPC, we have a standard set of functions we can call on them to do stuff. In this case, we can attack them, and check if they are alive or dead.
In action: PC.cs
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(Animator))]
public class PC : MonoBehaviour, person
{
Animator anim;
NavMeshAgent agent;
Vector2 smoothDeltaPosition = Vector2.zero;
Vector2 velocity = Vector2.zero;
public float health = 100;
public life_status Life = life_status.alive;
void Start()
{
anim = GetComponent<Animator>();
agent = GetComponent<NavMeshAgent>();
// Donāt update position automatically
agent.updatePosition = false;
}
void Update()
{
switch (Life)
{
case life_status.alive:
Vector3 worldDeltaPosition = agent.nextPosition - transform.position;
// Map 'worldDeltaPosition' to local space
float dx = Vector3.Dot(transform.right, worldDeltaPosition);
float dy = Vector3.Dot(transform.forward, worldDeltaPosition);
Vector2 deltaPosition = new Vector2(dx, dy);
// Low-pass filter the deltaMove
float smooth = Mathf.Min(1.0f, Time.deltaTime / 0.15f);
smoothDeltaPosition = Vector2.Lerp(smoothDeltaPosition, deltaPosition, smooth);
// Update velocity if time advances
if (Time.deltaTime > 1e-5f)
velocity = smoothDeltaPosition / Time.deltaTime;
bool shouldMove = velocity.magnitude > 0.5f && agent.remainingDistance > agent.radius;
// Update animation parameters
anim.SetBool("move", shouldMove);
anim.SetFloat("velx", velocity.x);
anim.SetFloat("vely", velocity.y);
GetComponent<Transform>().LookAt(agent.steeringTarget + transform.forward);
break;
case life_status.dead:
//Do Nothing?
break;
default:
break;
}
}
void OnAnimatorMove()
{
switch (Life)
{
case life_status.alive:
// Update position to agent position
transform.position = agent.nextPosition;
break;
case life_status.dead:
break;
default:
break;
}
}
public void attacked(int power, person Attacker)
{
Debug.Log(this + " attacked by " + Attacker + " for " + power + " damage.");
health -= power;
if (health <= 0)
{
Life = life_status.dead;
}
}
public life_status GetLifeStatus()
{
return Life;
}
}
The functions mandated by the interface at the bottom.
Similarly: NPC.cs
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(Animator))]
public class NPC : MonoBehaviour, person
{
Animator anim;
NavMeshAgent agent;
Vector2 smoothDeltaPosition = Vector2.zero;
Vector2 velocity = Vector2.zero;
public PC nav_target;
int health;
bool dead;
public int biteforce = 10;
void Start()
{
anim = GetComponent<Animator>();
agent = GetComponent<NavMeshAgent>();
// Donāt update position automatically
agent.updatePosition = false;
// Do have nav agent update rotation to look down path
agent.updateRotation = true;
}
Quaternion lastFrameRotation = Quaternion.identity;
bool readytomove = false;
bool bite = false;
void Update()
{
Vector3 worldDeltaPosition = agent.nextPosition - transform.position;
// Map 'worldDeltaPosition' to local space
float dx = Vector3.Dot(transform.right, worldDeltaPosition);
float dy = Vector3.Dot(transform.forward, worldDeltaPosition);
Vector2 deltaPosition = new Vector2(dx, dy);
// Low-pass filter the deltaMove
float smooth = Mathf.Min(1.0f, Time.deltaTime / 0.15f);
smoothDeltaPosition = Vector2.Lerp(smoothDeltaPosition, deltaPosition, smooth);
// Update velocity if time advances
if (Time.deltaTime > 1e-5f)
velocity = smoothDeltaPosition / Time.deltaTime;
Quaternion deltaRotation = transform.rotation * Quaternion.Inverse(lastFrameRotation);
Vector3 eulerRotation = new Vector3(
Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.x)),
Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.y)),
Mathf.DeltaAngle(0, Mathf.Round(deltaRotation.eulerAngles.z)));
lastFrameRotation = transform.rotation;
bool shouldMove = !agent.isStopped ;
//bool shouldMove = velocity.magnitude > 0.5f && agent.remainingDistance > agent.radius && eulerRotation.magnitude > 0.5f;
if (agent.remainingDistance < 0.5f)
{
Debug.Log(this + " is in range to attack " + nav_target);
//Within range to attack
if (nav_target.GetLifeStatus() == life_status.alive && !anim.GetBool("bite"))
{
//Notice that is will be called every frame.. Only do it when not in bite behavour?
Debug.Log(this + " attacking " + nav_target + " for " + biteforce + " damage.");
nav_target.attacked(biteforce, this);
anim.SetBool("bite", true);
}
}
// Update animation parameters
anim.SetBool("move", shouldMove);
anim.SetFloat("turn", eulerRotation.x);
anim.SetFloat("fwd", velocity.y);
GetComponent<Transform>().LookAt(agent.steeringTarget + transform.forward);
}
void OnAnimatorMove()
{
// Update position to agent position
transform.position = agent.nextPosition;
//Set destination once when agent hits the ground..
if (nav_target.GetLifeStatus() == life_status.alive )
{
agent.SetDestination(nav_target.transform.position);
}
}
public void attacked(int power, person Attacker)
{
Debug.Log(this + " attacked by " + Attacker + " for " + power + " damage.");
if (health > 0)
{
//Modify hit power based on deflection, armour?
// for now UnityChan squishy
health -= power;
}
else
{
health = 0;
}
}
public life_status GetLifeStatus()
{
if (dead)
return life_status.dead;
else
return life_status.alive;
}
}
Todo:
Zombie only attacks once. There is an issue where they can attack every frame, so I need to ensure they attack once, run out the animation, and then attack again.
Currently, they do not re-attack. Need to check over the code here. I am checking the state of the animator to check if they are in the bite animation state, and not biting if this is true. Maybe I need to auto-return from that state in the animator? Drat now I need to go check that.