Asked by Coder
using UnityEngine;
public class MonsterBehavior : MonoBehaviour
{
[Header("Target")]
public Transform target; // Assign player transform in inspector (or use tag auto-find below)
[Header("Movement")]
public float moveSpeed = 2f; // Movement speed (units/second)
public float stopDistance = 1.5f; // Distance at which the monster stops moving toward the target
[Header("Rotation")]
public float rotationSpeed = 5f; // Higher = faster turning
[Header("Options")]
public bool findPlayerByTag = true; // If true and target is null, tries to find GameObject tagged "Player"
public string playerTag = "Player";
// Optional: reference to animator to control walking animation
public Animator animator;
private readonly int animSpeedHash = Animator.StringToHash("Speed");
void Start()
{
if (target == null && findPlayerByTag)
{
GameObject p = GameObject.FindGameObjectWithTag(playerTag);
if (p != null) target = p.transform;
}
}
void Update()
{
if (target == null) return;
// Compute horizontal direction to player (ignore vertical difference for rotation)
Vector3 toTarget = target.position - transform.position;
Vector3 toTargetFlat = new Vector3(toTarget.x, 0f, toTarget.z);
float distance = toTargetFlat.magnitude;
// Smoothly rotate to face the player (only yaw / y-axis)
if (toTargetFlat.sqrMagnitude > 0.0001f)
{
Quaternion targetRotation = Quaternion.LookRotation(toTargetFlat.normalized);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// Move forward while farther than stopDistance
if (distance > stopDistance)
{
Vector3 forward = transform.forward;
Vector3 newPosition = transform.position + forward * moveSpeed * Time.deltaTime;
transform.position = newPosition;
if (animator) animator.SetFloat(animSpeedHash, 1f);
}
else
{
if (animator) animator.SetFloat(animSpeedHash, 0f);
}
}
// Optional: draw stopDistance in editor for convenience
void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, stopDistance);
}
}
Monster spazzes out and changes color when he gets close.
All Answers 1
Answered by
GPT-5 mini
AI
You're very close — the spazzing is almost certainly caused by the movement/rotation logic oscillating/overshooting when the monster gets within the stop radius, and the color change is likely a side-effect of the animator rapidly flipping states (or an animation keyframing the material/color) as the Speed parameter jumps between values. Below I explain what's happening and give a safer, corrected script plus a few extra tips.
Why it happens
- You move using transform.forward while you rotate toward the player. When the monster is close, small angle differences cause it to move a little past the stopping radius, then rotation flips it back, producing jitter/oscillation.
- The animator Speed parameter is set directly to 1 or 0. If it toggles rapidly at the threshold the animator might switch between clips that change the material or other properties, producing a color flicker.
- Slerp factor usage can also be problematic if interpreted as a speed; using Quaternion.Slerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime) treats rotationSpeed as a fraction per second (not degrees) — RotateTowards is more intuitive (degrees/sec).
Fixes
- Move toward the exact target position on the horizontal plane (Vector3.MoveTowards or CharacterController/ Rigidbody APIs) instead of using transform.forward.
- Rotate smoothly using Quaternion.RotateTowards (degrees/sec) or clamp the Slerp factor.
- Smooth the animator parameter using Animator.SetFloat with damping so it doesn't flip instantly.
- Ensure your animations do not keyframe material color, or remove that track if they do.
Corrected script (safe, simple changes)
Replace your Update() with this version (keeps inspector fields similar):
public float rotationSpeed = 180f; // degrees per second now
void Update()
{
if (target == null) return;
// horizontal positions only
Vector3 targetPosFlat = new Vector3(target.position.x, transform.position.y, target.position.z);
Vector3 toTarget = targetPosFlat - transform.position;
float distance = toTarget.magnitude;
// Smooth rotation: use RotateTowards (rotationSpeed is degrees/sec)
if (toTarget.sqrMagnitude > 0.0001f)
{
Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// Move directly toward the player to avoid overshoot/oscillation
if (distance > stopDistance)
{
Vector3 next = Vector3.MoveTowards(transform.position, targetPosFlat, moveSpeed * Time.deltaTime);
transform.position = next;
// Smooth animator parameter (damp time 0.1s)
if (animator) animator.SetFloat(animSpeedHash, 1f, 0.1f, Time.deltaTime);
}
else
{
if (animator) animator.SetFloat(animSpeedHash, 0f, 0.1f, Time.deltaTime);
}
}
Why this helps
- MoveTowards always moves straight to the target position (no overshoot due to forward direction mismatch).
- RotateTowards uses degrees/sec which is clearer and avoids weird Slerp factor behavior.
- Animator.SetFloat with damping prevents rapid toggling and removes abrupt animation transitions that might affect color.
Extra suggestions
- If your monster uses physics, move it with Rigidbody.MovePosition or a CharacterController rather than transform.position.
- If you want pathfinding and obstacle avoidance, use a NavMeshAgent — it handles rotation/movement/stopping nicely.
- Double-check your animation clips in the Animator for any material/color keyframes. If present, remove them or use a separate Animator layer for material changes.
- If the color change persists, add a small debug: print the animator state or check if any script changes material.color when within range.
If you want, paste your Animator Controller or the exact animation clips — I can point out any color keys or show how to smooth transitions further.
Why it happens
- You move using transform.forward while you rotate toward the player. When the monster is close, small angle differences cause it to move a little past the stopping radius, then rotation flips it back, producing jitter/oscillation.
- The animator Speed parameter is set directly to 1 or 0. If it toggles rapidly at the threshold the animator might switch between clips that change the material or other properties, producing a color flicker.
- Slerp factor usage can also be problematic if interpreted as a speed; using Quaternion.Slerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime) treats rotationSpeed as a fraction per second (not degrees) — RotateTowards is more intuitive (degrees/sec).
Fixes
- Move toward the exact target position on the horizontal plane (Vector3.MoveTowards or CharacterController/ Rigidbody APIs) instead of using transform.forward.
- Rotate smoothly using Quaternion.RotateTowards (degrees/sec) or clamp the Slerp factor.
- Smooth the animator parameter using Animator.SetFloat with damping so it doesn't flip instantly.
- Ensure your animations do not keyframe material color, or remove that track if they do.
Corrected script (safe, simple changes)
Replace your Update() with this version (keeps inspector fields similar):
public float rotationSpeed = 180f; // degrees per second now
void Update()
{
if (target == null) return;
// horizontal positions only
Vector3 targetPosFlat = new Vector3(target.position.x, transform.position.y, target.position.z);
Vector3 toTarget = targetPosFlat - transform.position;
float distance = toTarget.magnitude;
// Smooth rotation: use RotateTowards (rotationSpeed is degrees/sec)
if (toTarget.sqrMagnitude > 0.0001f)
{
Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// Move directly toward the player to avoid overshoot/oscillation
if (distance > stopDistance)
{
Vector3 next = Vector3.MoveTowards(transform.position, targetPosFlat, moveSpeed * Time.deltaTime);
transform.position = next;
// Smooth animator parameter (damp time 0.1s)
if (animator) animator.SetFloat(animSpeedHash, 1f, 0.1f, Time.deltaTime);
}
else
{
if (animator) animator.SetFloat(animSpeedHash, 0f, 0.1f, Time.deltaTime);
}
}
Why this helps
- MoveTowards always moves straight to the target position (no overshoot due to forward direction mismatch).
- RotateTowards uses degrees/sec which is clearer and avoids weird Slerp factor behavior.
- Animator.SetFloat with damping prevents rapid toggling and removes abrupt animation transitions that might affect color.
Extra suggestions
- If your monster uses physics, move it with Rigidbody.MovePosition or a CharacterController rather than transform.position.
- If you want pathfinding and obstacle avoidance, use a NavMeshAgent — it handles rotation/movement/stopping nicely.
- Double-check your animation clips in the Animator for any material/color keyframes. If present, remove them or use a separate Animator layer for material changes.
- If the color change persists, add a small debug: print the animator state or check if any script changes material.color when within range.
If you want, paste your Animator Controller or the exact animation clips — I can point out any color keys or show how to smooth transitions further.
Submit Your Answer
We prioritize human answers over AI answers.
If you are human, and you can answer this question, please submit your answer.