Jamie Greunbaum 690c5a40c2 - Added chicken sounds to the chickens.
- Began work on a game room so the world can be more of a hangout area.
2026-02-04 03:46:14 -05:00

294 lines
6.8 KiB
C#

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon.Common;
public enum ChickenState
{
Idle,
Walk,
Run,
Eat,
Held,
Airborne
}
public class Chimken : UdonSharpBehaviour
{
[SerializeField] private AudioSource _AudioSource;
[SerializeField] private TimerSounds _IdleSoundsTimer;
[SerializeField] private TimerSounds _HeldSoundsTimer;
[SerializeField] private TimerSounds _AirborneSoundsTimer;
[Space]
[SerializeField] private string _FollowUser = "";
[Space]
[SerializeField] private Transform _GroundRayStart;
[SerializeField] private Transform _GroundRayEnd;
[SerializeField] private Transform _WallRayStart;
[SerializeField] private Transform _WallRayEnd;
[UdonSynced, FieldChangeCallback(nameof(AnimationState))] private ChickenState _AnimationState = 0;
[UdonSynced] private bool _IsBeingHeld = false;
[UdonSynced] private float _MovementDirectionDegrees = 0.0f;
private VRCPlayerApi _PlayerToFollow = null;
private bool _Moving = false;
private float _MovementTimer = 0.0f;
private Animator _Animator = null;
private float _WallColliderTimer = 0.0f;
private const float WALK_SPEED = 0.35f;
private const float RUN_SPEED = 1.4f;
private const float TURN_LERP_SPEED = 0.15f;
void Start()
{
_Animator = GetComponent<Animator>();
}
void FixedUpdate()
{
bool Airborne = true;
if (Networking.IsOwner(gameObject))
{
RaycastHit HitInfo;
// Ground collision detection
if (_IsBeingHeld)
{
SetAnimationState(ChickenState.Held);
}
else if (Physics.Raycast(_GroundRayStart.position, _GroundRayStart.forward, out HitInfo, _GroundRayEnd.localPosition.z))
{
transform.position = HitInfo.point;
Airborne = false;
}
else
{
SetAnimationState(ChickenState.Airborne);
}
// Wall collision detection
_WallColliderTimer -= Time.fixedDeltaTime;
if (_WallColliderTimer <= 0.0f && _Moving)
{
if (Physics.Raycast(_WallRayStart.position, _WallRayStart.forward, out HitInfo, _WallRayEnd.localPosition.z))
{
float OldMovementDirection = _MovementDirectionDegrees;
Vector3 ReflectedAngle = Vector3.Reflect(_WallRayStart.forward, HitInfo.normal);
_MovementDirectionDegrees = Vector3.SignedAngle(Vector3.forward, ReflectedAngle, Vector3.up);
}
_WallColliderTimer = 0.25f;
}
}
Vector3 PlayerPosition;
Vector3 PlayerPositionLateral;
GetFollowPlayerPositions(out PlayerPosition, out PlayerPositionLateral);
Vector3 ChickenPositionLateral = new Vector3(transform.position.x, 0.0f, transform.position.z);
if (!Airborne)
{
if (_PlayerToFollow != null)
{
float DistanceToPlayer = Vector3.Distance(ChickenPositionLateral, PlayerPositionLateral);
if (DistanceToPlayer <= 0.7f)
{
SetAnimationState(ChickenState.Eat);
}
else
{
SetAnimationState(DistanceToPlayer <= 2.75f ? ChickenState.Walk : ChickenState.Run);
}
}
else
{
_MovementTimer -= Time.deltaTime;
if (_MovementTimer <= 0.0f && Networking.IsOwner(gameObject))
{
SetAnimationState((ChickenState)Random.Range(0, 4));
}
}
}
if (_Moving)
{
switch (_AnimationState)
{
case ChickenState.Walk:
transform.position += (transform.forward * Time.fixedDeltaTime * WALK_SPEED);
break;
case ChickenState.Run:
transform.position += (transform.forward * Time.fixedDeltaTime * RUN_SPEED);
break;
case ChickenState.Airborne:
transform.position += (transform.forward * Time.fixedDeltaTime * RUN_SPEED);
break;
}
if (_PlayerToFollow == null)
{
transform.rotation = Quaternion.Euler(
transform.eulerAngles.x,
Mathf.LerpAngle(transform.rotation.eulerAngles.y, _MovementDirectionDegrees, TURN_LERP_SPEED),
transform.eulerAngles.z);
}
else
{
float Angle = Vector3.SignedAngle(transform.forward, (PlayerPositionLateral - ChickenPositionLateral).normalized, transform.up);
transform.rotation = Quaternion.Euler(
transform.eulerAngles.x,
Mathf.LerpAngle(transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.y + Angle, TURN_LERP_SPEED),
transform.eulerAngles.z);
}
}
// Reset roll and pitch to zero on every physics update so the chicken stays upright
Vector3 Rotation = transform.eulerAngles;
Rotation.x = 0.0f;
Rotation.z = 0.0f;
transform.rotation = Quaternion.Euler(Rotation);
}
public override void OnPlayerJoined(VRCPlayerApi Player)
{
if (Player.displayName == _FollowUser)
{
_PlayerToFollow = Player;
}
base.OnPlayerJoined(Player);
}
public override void OnPlayerLeft(VRCPlayerApi Player)
{
if (Player.displayName == _FollowUser)
{
_PlayerToFollow = null;
}
base.OnPlayerLeft(Player);
}
public override void OnPickup()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
SetAnimationState(ChickenState.Held);
_IsBeingHeld = true;
base.OnPickup();
}
public override void OnDrop()
{
_IsBeingHeld = false;
base.OnDrop();
}
private void SetAnimationState(ChickenState State)
{
if (Networking.IsOwner(gameObject) && AnimationState != State)
{
AnimationState = State;
_MovementDirectionDegrees = Random.Range(0.0f, 360.0f - float.MinValue);
RequestSerialization();
}
}
private void OnStateChanged()
{
_Animator.SetInteger("State", (int)AnimationState);
_Moving = false;
switch (AnimationState)
{
case ChickenState.Idle:
_MovementTimer = 4.0f;
PlayIdleSounds();
break;
case ChickenState.Walk:
_MovementTimer = 4.5f;
_Moving = true;
PlayIdleSounds();
break;
case ChickenState.Run:
_MovementTimer = 1.2f;
_Moving = true;
PlayIdleSounds();
break;
case ChickenState.Eat:
_MovementTimer = 6.0f;
PlayIdleSounds();
break;
case ChickenState.Held:
_MovementTimer = 0.0f;
PlayHeldSounds();
break;
case ChickenState.Airborne:
_MovementTimer = 0.0f;
_Moving = true;
PlayAirborneSounds();
break;
}
}
private bool GetFollowPlayerPositions(out Vector3 PlayerPosition, out Vector3 PlayerPositionLateral)
{
if (_PlayerToFollow != null)
{
PlayerPosition = _PlayerToFollow.GetPosition();
PlayerPositionLateral = new Vector3(PlayerPosition.x, 0.0f, PlayerPosition.z);
return true;
}
PlayerPosition = Vector3.zero;
PlayerPositionLateral = Vector3.zero;
return false;
}
private void PlayIdleSounds()
{
_HeldSoundsTimer.StopTimer();
_AirborneSoundsTimer.StopTimer();
_IdleSoundsTimer.StartTimer();
}
private void PlayHeldSounds()
{
_IdleSoundsTimer.StopTimer();
_AirborneSoundsTimer.StopTimer();
_HeldSoundsTimer.StartTimer();
}
private void PlayAirborneSounds()
{
_IdleSoundsTimer.StopTimer();
_HeldSoundsTimer.StopTimer();
_AirborneSoundsTimer.StartTimer();
}
private ChickenState AnimationState
{
set
{
_AnimationState = value;
OnStateChanged();
}
get => _AnimationState;
}
}