designed and developed code to sync enemy attacks with animation
player visual feedback
enemy visual feedback
player functionality to interact with environment and enemies
My Contributions:
Animation Syncing
designed and developed code to sync enemy attacks with animation MANUALLY. Since at the
time didn't know about animation events that unity has builtin
Flash Behaviour
Coded the functionality behind this very simple mechanic in our game to give a bit more
player feedback
Candy
implemented the functionality behind this just to show how many points the player recieves
when defeating an enemy
Animation Sync Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BirdBehaviour : MonoBehaviour
{
public BirdBehaviour bb;
[Tooltip("Reference to the projectile that the bird shoots")]
[SerializeField]
private GameObject Projectile;
[Tooltip("The difference between the bird origin and the projectile spawn point ")]
[SerializeField]
private float ProjectileOffset;
//the current projectile
public List<GameObject> CurrentProjectiles = new List<GameObject>();
[Tooltip("the different colors the pelican can be")]
[SerializeField]
private List<Material> Colors;
[Tooltip("Reference to the beak")]
[SerializeField]
private GameObject Beak;
[Tooltip("This will shut off some functions of the full bird")]
[SerializeField]
private bool isTutorial;
[Tooltip("represents what the tutorial bird is trying to do ")]
[SerializeField]
private GameObject TutorialWaiter;
private Animator playerAnimator;
public int currentFrame = 0;
AnimatorClipInfo[] animationClip;
int amountTimeLooped=0;
[SerializeField] private bool spawnedOne;
bool previousAttacked;
private bool startingAttack;
public int attackNum = 0;
private void FixedUpdate()
{
if (playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack"))
{
animationClip = playerAnimator.GetCurrentAnimatorClipInfo(0);
if (playerAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime - amountTimeLooped > 1)
{
amountTimeLooped++;
}
//code to convert the normalized time into frames so i can get exact frame to spawn projectile
currentFrame = (int)((playerAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime - amountTimeLooped) * (animationClip[0].clip.length * 24));
}
else if(amountTimeLooped != 0)
{
amountTimeLooped = 0;
}
}
// Update is called once per frame
void Update()
{
Rotate();
if (bb != null && bb.playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack"))
{
previousAttacked = true;
}
else if (bb != null && !bb.playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && !playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && previousAttacked && !startingAttack)
{
startingAttack = true;
previousAttacked = false;
StartCoroutine(Attack());
}
if (!playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && TutorialWaiter == null && isTutorial && !startingAttack)
{
startingAttack = true;
StartCoroutine(Attack());
}
else if (playerAnimator.GetCurrentAnimatorStateInfo(0).IsName("Attack")
&& currentFrame == 123 && !spawnedOne)
{
//frame 123 is the frame of the animation that the projectile spawns
CheckProjectile();
}
else if (currentFrame >= 124)
{
spawnedOne = false;
startingAttack = false;
}
}
/// <summary>
/// Checks if has spawned a projectile already which if not it spawns one
/// and makes it so it cant spawn another until its reset
/// <summary>
private void CheckProjectile()
{
if (!spawnedOne)
{
CurrentProjectiles.Add(Instantiate(Projectile, Beak.transform.position + (transform.forward * ProjectileOffset), transform.rotation));
CurrentProjectiles[CurrentProjectiles.Count - 1].GetComponent<BirdProjectileScript>().ConnectToBird(this);
spawnedOne = !spawnedOne;
}
}
public IEnumerator Attack()
{
attackNum++;
if (gameObject.name != "Tutorial_Bird")
{
/Ommited code for this but it basically just does the sound for regular bird
StartCoroutine(AttackSound(attackNum));
}
else
{
//Ommited code for this but it basically just does the sound for tutorial bird
StartCoroutine(TutorialAttackSound(attackNum));
}
yield return new WaitForSeconds(.4f);
playerAnimator.SetTrigger("AttackTrigger");
}
Flash Behaviour Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[SerializeField]
private SkinnedMeshRenderer[] Body;
[SerializeField]
private Material flash;
[SerializeField]
private Material originalMaterial;
[Tooltip("The particle appears after the hyena is destroyed")]
[SerializeField]
private GameObject deathParticle1;
[Tooltip("The object that appears after the hyena is destroyed")]
[SerializeField]
private GameObject shatteredEnemy;
/// <summary>
///
/// <summary>
///<param name="collision"><param>
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag.Equals("Bat"))
{
FindObjectOfType<AudioManager>().Play("Hit_Enemy");
lives--;
//would play the reaction animation of the enemy but i ommited
HitReaction();
Flash();
}
}
private void HitReaction()
{
if (lives <= 0)
{
FindObjectOfType<AudioManager>().Play("Enemy_Death" + Random.Range(0, 4).ToString());
Destroy(Instantiate(deathParticle1, transform.position, Quaternion.identity), 15f);
Destroy(Instantiate(shatteredEnemy, transform.position, transform.rotation), 5f);
pb.AddScore(points);
Destroy(gameObject);
}
}
private void Flash()
{
foreach(SkinnedMeshRenderer g in Body)
{
//since all material in body was the same can just do this
g.material = flash;
}
StartCoroutine(EndFlash());
}
private IEnumerator EndFlash()
{
yield return new WaitForSeconds(.25f);
foreach (SkinnedMeshRenderer g in Body)
{
//since all material in body was the same can just do this
g.material = originalMaterial;
}
yield return null;
}