Skip to content

Instantly share code, notes, and snippets.

@csumsky3
Created February 20, 2019 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save csumsky3/9916618e5095b2b6bf176c3aefa2d97d to your computer and use it in GitHub Desktop.
Save csumsky3/9916618e5095b2b6bf176c3aefa2d97d to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using System.Linq;
using Spine;
using Spine.Unity;
using Spine.Unity.Modules.AttachmentTools;
using UnityEngine;
public class BAgentAnimator : MonoBehaviour
{
const string footstepEventName = "footstep";
const float distanceThreshold = 50f; //this might could even be lowered
[SerializeField] SkeletonAnimation frontAnimation = null;
[SerializeField] SkeletonAnimation backAnimation = null;
[SerializeField] SkeletonAnimation leftAnimation = null;
[SerializeField] SkeletonAnimation rightAnimation = null;
[SerializeField] bool listenForFootsteps = false;
MeshRenderer frontMesh;
MeshRenderer backMesh;
MeshRenderer leftMesh;
MeshRenderer rightMesh;
Slot frontCarrySlot;
Slot backCarrySlot;
Slot leftCarrySlot;
Slot rightCarrySlot;
bool isSpriteling;
bool isRendering;
public bool IsPaused { get; private set; }
public LockState AnimationLock = new LockState();
#region Monobehaviour
void Awake()
{
frontMesh = frontAnimation.GetComponent<MeshRenderer>();
backMesh = backAnimation.GetComponent<MeshRenderer>();
leftMesh = leftAnimation.GetComponent<MeshRenderer>();
rightMesh = rightAnimation.GetComponent<MeshRenderer>();
if (frontMesh == null || backMesh == null || leftMesh == null || rightMesh == null) {
Debug.LogWarning("This agent doesn't have all its meshes assigned!");
}
isSpriteling = GetComponent<BSpriteling>();
isRendering = true;
}
void Start()
{
const string carrySlotName = "carry_slot";
frontCarrySlot = frontAnimation.skeleton.FindSlot(carrySlotName);
backCarrySlot = backAnimation.skeleton.FindSlot(carrySlotName);
leftCarrySlot = leftAnimation.skeleton.FindSlot(carrySlotName);
rightCarrySlot = rightAnimation.skeleton.FindSlot(carrySlotName);
if (listenForFootsteps) {
assignFootstepListeners(frontAnimation);
assignFootstepListeners(backAnimation);
assignFootstepListeners(leftAnimation);
assignFootstepListeners(rightAnimation);
}
}
void Update()
{
if (isSpriteling) {
var distanceToPlayer = Vector3.Distance(transform.position, BGameManager.Instance.Player.transform.position);
if (isRendering && distanceToPlayer > distanceThreshold) {
setAnimationsEnabled(false);
} else if (!isRendering && distanceToPlayer <= distanceThreshold) {
setAnimationsEnabled(true);
}
}
}
void OnDestroy()
{
if (listenForFootsteps) {
frontAnimation.AnimationState.Event -= HandleEvent;
}
}
#endregion
#region public functions
//TODO there's something wrong with he _onEnd call if a _nextAnim gets passed in. I think something to do with default mix duration?
public void SetAnimation(string _animationName, string _nextAnimation = "", bool _attachCarryableToSkel = false, System.Action _onEnd = null, float _mixDuration = 0.2f)
{
if (AnimationLock.Locked) {
return;
}
//First, if the agent is carrying something, check if they have unique carrying anims, and update the anim name if so
if (_attachCarryableToSkel && (_animationName == "Idle" || _animationName == "Walk")) {
const string prefixToUse = "Carry_";
string updatedAnimationName = string.Format("{0}{1}", prefixToUse, _animationName);
//Only check the frontAnim since we'll assume that all animation meshes will have a carry, if the agent should have one
if (frontAnimation.HasClip(updatedAnimationName)) {
_animationName = updatedAnimationName;
}
}
//Now set the proper new animation to play, if the skel has it
if (frontAnimation.AnimationName != _animationName && frontAnimation.HasClip(_animationName)) {
var playNextAnim = !string.IsNullOrEmpty(_nextAnimation) && frontAnimation.HasClip(_nextAnimation);
var te = frontAnimation.state.SetAnimation(0, _animationName, !playNextAnim);
te.MixDuration = _mixDuration;
te.Complete += delegate {
_onEnd?.Invoke();
};
if (playNextAnim) {
frontAnimation.state.AddAnimation(0, _nextAnimation, true, 0);
}
}
if (backAnimation.AnimationName != _animationName && backAnimation.HasClip(_animationName)) {
var playNextAnim = !string.IsNullOrEmpty(_nextAnimation) && backAnimation.HasClip(_nextAnimation);
var te = backAnimation.state.SetAnimation(0, _animationName, !playNextAnim);
te.MixDuration = _mixDuration;
te.Complete += delegate {
_onEnd?.Invoke();
};
if (playNextAnim) {
backAnimation.state.AddAnimation(0, _nextAnimation, true, 0);
}
}
if (leftAnimation.AnimationName != _animationName && leftAnimation.HasClip(_animationName)) {
var playNextAnim = !string.IsNullOrEmpty(_nextAnimation) && leftAnimation.HasClip(_nextAnimation);
var te = leftAnimation.state.SetAnimation(0, _animationName, !playNextAnim);
te.MixDuration = _mixDuration;
te.Complete += delegate {
_onEnd?.Invoke();
};
if (playNextAnim) {
leftAnimation.state.AddAnimation(0, _nextAnimation, true, 0);
}
}
if (rightAnimation.AnimationName != _animationName && rightAnimation.HasClip(_animationName)) {
var playNextAnim = !string.IsNullOrEmpty(_nextAnimation) && rightAnimation.HasClip(_nextAnimation);
var te = rightAnimation.state.SetAnimation(0, _animationName, !playNextAnim);
te.MixDuration = _mixDuration;
te.Complete += delegate {
_onEnd?.Invoke();
};
if (playNextAnim) {
rightAnimation.state.AddAnimation(0, _nextAnimation, true, 0);
}
}
}
public float GetDurationForAnimation(string _animation, FacingDirection _direction)
{
switch (_direction) {
case FacingDirection.Down:
return frontAnimation != null ? frontAnimation.GetTimeForClip(_animation) : -1f;
case FacingDirection.Left:
return leftAnimation != null ? leftAnimation.GetTimeForClip(_animation) : -1f;
case FacingDirection.Up:
return backAnimation != null ? backAnimation.GetTimeForClip(_animation) : -1f;
case FacingDirection.Right:
return rightAnimation != null ? rightAnimation.GetTimeForClip(_animation) : -1f;
}
return -1f;
}
public string[] GetAllAnimations()
{
var collection = new HashSet<string>();
foreach (var anim in frontAnimation.skeleton.Data.animations.Items) {
collection.Add(anim.name);
}
foreach (var anim in backAnimation.skeleton.Data.animations.Items) {
collection.Add(anim.name);
}
foreach (var anim in leftAnimation.skeleton.Data.animations.Items) {
collection.Add(anim.name);
}
foreach (var anim in rightAnimation.skeleton.Data.animations.Items) {
collection.Add(anim.name);
}
return collection.ToArray();
}
public void Pause()
{
//Now set the proper new animation to play, if the skel has it
if (!IsPaused) {
IsPaused = true;
frontAnimation.timeScale = 0f;
backAnimation.timeScale = 0f;
leftAnimation.timeScale = 0f;
rightAnimation.timeScale = 0f;
}
}
public void Unpause()
{
//Now set the proper new animation to play, if the skel has it
if (IsPaused) {
IsPaused = false;
frontAnimation.timeScale = 1f;
backAnimation.timeScale = 1f;
leftAnimation.timeScale = 1f;
rightAnimation.timeScale = 1f;
}
}
public void TogglePause()
{
//Now set the proper new animation to play, if the skel has it
if (!IsPaused) {
IsPaused = true;
frontAnimation.timeScale = 0f;
backAnimation.timeScale = 0f;
leftAnimation.timeScale = 0f;
rightAnimation.timeScale = 0f;
} else {
IsPaused = false;
frontAnimation.timeScale = 1f;
backAnimation.timeScale = 1f;
leftAnimation.timeScale = 1f;
rightAnimation.timeScale = 1f;
}
}
public void SetMeshForFacing(FacingDirection _direction)
{
if (isRendering) {
setFrontEnabled(_direction == FacingDirection.Down);
setBackEnabled(_direction == FacingDirection.Up);
setLeftEnabled(_direction == FacingDirection.Left);
setRightEnabled(_direction == FacingDirection.Right);
}
}
public bool AttachCarryableToSkel(System.Enum _carryableType)
{
var sprite = getCarryableSpriteFromType(_carryableType);
if (sprite != null) {
attachSpriteToSkeleton(frontAnimation, frontMesh, frontCarrySlot, sprite);
attachSpriteToSkeleton(backAnimation, backMesh, backCarrySlot, sprite);
attachSpriteToSkeleton(leftAnimation, leftMesh, leftCarrySlot, sprite);
attachSpriteToSkeleton(rightAnimation, rightMesh, rightCarrySlot, sprite);
return true;
} else {
return false;
}
}
public void UnattachCarryableFromSkel()
{
unattachSpriteFromSkeleton(frontAnimation, frontCarrySlot);
unattachSpriteFromSkeleton(backAnimation, backCarrySlot);
unattachSpriteFromSkeleton(leftAnimation, leftCarrySlot);
unattachSpriteFromSkeleton(rightAnimation, rightCarrySlot);
}
public void SetTimeScale(float _timeScale)
{
frontAnimation.AnimationState.TimeScale = _timeScale;
backAnimation.AnimationState.TimeScale = _timeScale;
leftAnimation.AnimationState.TimeScale = _timeScale;
rightAnimation.AnimationState.TimeScale = _timeScale;
}
#endregion
#region private helpers
Sprite getCarryableSpriteFromType(System.Enum _carryableType) => Resources.Load<Sprite>(string.Format("Sprites/{0}", _carryableType.ToString()));
void attachSpriteToSkeleton(SkeletonAnimation _skelAnim, MeshRenderer _mesh, Slot _slot, Sprite _sprite)
{
//Get handle to a few things that we'll be using it a lot
Skeleton skel = _skelAnim.skeleton;
string slotName = _slot.Data.Name;
string newAttachmentName = _sprite.name;
//All attachment changes will be applied to the skin. We use a clone so other instances will not be affected.
Skin newSkin = skel.UnshareSkin(true, false);
//Create an attachment from a Unity Sprite
RegionAttachment newAttachment = _sprite.ToRegionAttachmentPMAClone(_mesh.material);
//Add the attachement to the slot
int slotIndex = _skelAnim.skeleton.FindSlotIndex(slotName);
newSkin.AddAttachment(slotIndex, newAttachmentName, newAttachment);
//Set the skin to the one we made, and then make our attachments visible.
//Note that any keying to the slot in the anim will still be recognized, so make sure that's all good to go!
skel.SetSkin(newSkin);
skel.SetToSetupPose();
skel.SetAttachment(slotName, newAttachmentName);
}
void unattachSpriteFromSkeleton(SkeletonAnimation _skelAnim, Slot _slot)
{
Skeleton skel = _skelAnim.skeleton;
skel.SetSlotAttachmentToSetupPose(skel.FindSlotIndex(_slot.Data.Name));
}
void assignFootstepListeners(SkeletonAnimation _skelAnim)
{
_skelAnim.AnimationState.Event += HandleEvent;
}
void HandleEvent(TrackEntry trackEntry, Spine.Event e)
{
// Play some sound if the event named "footstep" fired.
if (e.Data.Name == footstepEventName) {
BFMODInterfacer.Instance.PlayPCFootstep();
}
}
void setAnimationsEnabled(bool _enabled)
{
setFrontEnabled(_enabled);
setBackEnabled(_enabled);
setLeftEnabled(_enabled);
setRightEnabled(_enabled);
isRendering = _enabled;
}
void setFrontEnabled(bool _enabled)
{
frontMesh.enabled = _enabled;
frontAnimation.enabled = _enabled;
}
void setBackEnabled(bool _enabled)
{
backMesh.enabled = _enabled;
backAnimation.enabled = _enabled;
}
void setLeftEnabled(bool _enabled)
{
leftMesh.enabled = _enabled;
leftAnimation.enabled = _enabled;
}
void setRightEnabled(bool _enabled)
{
rightMesh.enabled = _enabled;
rightAnimation.enabled = _enabled;
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment