added gun models and enemy spawner

This commit is contained in:
2026-02-11 16:21:45 +00:00
parent 2d70da4827
commit 50968843e5
16 changed files with 1372 additions and 189 deletions

View File

@@ -0,0 +1,394 @@
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CharacterController))]
public class HumanoidEnemy : MonoBehaviour
{
public enum EnemyType { Grunt, Brute, Runner }
[Header("Enemy Type")]
public EnemyType enemyType = EnemyType.Grunt;
[Header("Movement")]
public float moveSpeed = 5f;
public float chaseRange = 25f;
public float attackRange = 2f;
public float gravity = -20f;
[Header("Combat")]
public float attackDamage = 10f;
public float attackCooldown = 1f;
[Header("Patrol (optional)")]
public float patrolRadius = 8f;
public float patrolWaitTime = 2f;
// Internal
private Transform player;
private CharacterController controller;
private EnemyHealth health;
private Vector3 spawnPoint;
private Vector3 patrolTarget;
private float nextAttackTime;
private float verticalVelocity;
private float patrolWaitTimer;
private bool isAggro = false;
// Animation state
private Transform bodyRoot;
private Transform headTransform;
private Transform torsoTransform;
private Transform leftArm;
private Transform rightArm;
private Transform leftLeg;
private Transform rightLeg;
private float animTimer = 0f;
void Start()
{
controller = GetComponent<CharacterController>();
health = GetComponent<EnemyHealth>();
// Find player
GameObject playerObj = GameObject.Find("Player");
if (playerObj != null)
player = playerObj.transform;
spawnPoint = transform.position;
patrolTarget = GetRandomPatrolPoint();
ApplyTypeStats();
BuildHumanoidModel();
}
void ApplyTypeStats()
{
switch (enemyType)
{
case EnemyType.Grunt:
moveSpeed = 4f;
attackDamage = 10f;
attackCooldown = 1.2f;
chaseRange = 20f;
if (health != null) health.maxHealth = 80f;
break;
case EnemyType.Brute:
moveSpeed = 2.5f;
attackDamage = 25f;
attackCooldown = 2f;
chaseRange = 18f;
if (health != null) health.maxHealth = 200f;
break;
case EnemyType.Runner:
moveSpeed = 8f;
attackDamage = 8f;
attackCooldown = 0.6f;
chaseRange = 30f;
if (health != null) health.maxHealth = 50f;
break;
}
if (health != null)
health.currentHealth = health.maxHealth;
}
void Update()
{
if (health != null && health.currentHealth <= 0f) return;
if (player == null) return;
float distToPlayer = Vector3.Distance(transform.position, player.position);
// Aggro check
if (distToPlayer <= chaseRange)
isAggro = true;
if (isAggro)
{
if (distToPlayer <= attackRange)
Attack();
else
ChasePlayer();
}
else
{
Patrol();
}
// Gravity
if (controller.isGrounded && verticalVelocity < 0f)
verticalVelocity = -2f;
verticalVelocity += gravity * Time.deltaTime;
controller.Move(Vector3.up * verticalVelocity * Time.deltaTime);
// Animate
AnimateModel(isAggro && distToPlayer > attackRange);
}
void ChasePlayer()
{
Vector3 direction = (player.position - transform.position);
direction.y = 0f;
direction.Normalize();
controller.Move(direction * moveSpeed * Time.deltaTime);
// Face the player
if (direction != Vector3.zero)
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 8f);
}
void Patrol()
{
float distToTarget = Vector3.Distance(transform.position, patrolTarget);
if (distToTarget < 1.5f)
{
patrolWaitTimer += Time.deltaTime;
if (patrolWaitTimer >= patrolWaitTime)
{
patrolTarget = GetRandomPatrolPoint();
patrolWaitTimer = 0f;
}
return;
}
Vector3 direction = (patrolTarget - transform.position);
direction.y = 0f;
direction.Normalize();
controller.Move(direction * (moveSpeed * 0.4f) * Time.deltaTime);
if (direction != Vector3.zero)
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 4f);
}
Vector3 GetRandomPatrolPoint()
{
Vector2 randomCircle = Random.insideUnitCircle * patrolRadius;
return spawnPoint + new Vector3(randomCircle.x, 0f, randomCircle.y);
}
void Attack()
{
// Face the player
Vector3 direction = (player.position - transform.position);
direction.y = 0f;
if (direction != Vector3.zero)
transform.rotation = Quaternion.LookRotation(direction);
if (Time.time >= nextAttackTime)
{
nextAttackTime = Time.time + attackCooldown;
Debug.Log($"{gameObject.name} attacks for {attackDamage} damage!");
// Punch animation
StartCoroutine(PunchAnimation());
// TODO: Hook into player health system when you have one
// player.GetComponent<PlayerHealth>()?.TakeDamage(attackDamage);
}
}
// ─── Procedural Humanoid Model ───
void BuildHumanoidModel()
{
// Sizing based on type
float scale = 1f;
Color skinColor = new Color(0.6f, 0.45f, 0.35f);
Color shirtColor;
Color pantsColor = new Color(0.2f, 0.2f, 0.25f);
switch (enemyType)
{
case EnemyType.Grunt:
scale = 1f;
shirtColor = new Color(0.4f, 0.25f, 0.2f); // brown shirt
break;
case EnemyType.Brute:
scale = 1.3f;
shirtColor = new Color(0.5f, 0.1f, 0.1f); // red shirt
skinColor = new Color(0.5f, 0.35f, 0.3f);
break;
case EnemyType.Runner:
scale = 0.9f;
shirtColor = new Color(0.2f, 0.35f, 0.2f); // green shirt
break;
default:
shirtColor = Color.gray;
break;
}
bodyRoot = new GameObject("BodyRoot").transform;
bodyRoot.SetParent(transform);
bodyRoot.localPosition = Vector3.zero;
bodyRoot.localRotation = Quaternion.identity;
// Head
headTransform = CreatePart("Head", bodyRoot,
new Vector3(0f, 1.65f, 0f) * scale,
new Vector3(0.3f, 0.3f, 0.3f) * scale,
skinColor, PrimitiveType.Sphere);
// Eyes (two small dark spheres)
CreatePart("LeftEye", headTransform,
new Vector3(-0.08f, 0.03f, 0.12f),
new Vector3(0.08f, 0.08f, 0.05f),
new Color(0.9f, 0.1f, 0.1f), PrimitiveType.Sphere);
CreatePart("RightEye", headTransform,
new Vector3(0.08f, 0.03f, 0.12f),
new Vector3(0.08f, 0.08f, 0.05f),
new Color(0.9f, 0.1f, 0.1f), PrimitiveType.Sphere);
// Torso
torsoTransform = CreatePart("Torso", bodyRoot,
new Vector3(0f, 1.15f, 0f) * scale,
new Vector3(0.45f, 0.6f, 0.25f) * scale,
shirtColor, PrimitiveType.Cube);
// Left Arm
leftArm = CreatePart("LeftArm", bodyRoot,
new Vector3(-0.35f, 1.15f, 0f) * scale,
new Vector3(0.15f, 0.55f, 0.15f) * scale,
skinColor, PrimitiveType.Cube);
// Right Arm
rightArm = CreatePart("RightArm", bodyRoot,
new Vector3(0.35f, 1.15f, 0f) * scale,
new Vector3(0.15f, 0.55f, 0.15f) * scale,
skinColor, PrimitiveType.Cube);
// Left Leg
leftLeg = CreatePart("LeftLeg", bodyRoot,
new Vector3(-0.12f, 0.4f, 0f) * scale,
new Vector3(0.18f, 0.6f, 0.18f) * scale,
pantsColor, PrimitiveType.Cube);
// Right Leg
rightLeg = CreatePart("RightLeg", bodyRoot,
new Vector3(0.12f, 0.4f, 0f) * scale,
new Vector3(0.18f, 0.6f, 0.18f) * scale,
pantsColor, PrimitiveType.Cube);
// Adjust CharacterController to fit
controller.height = 1.9f * scale;
controller.radius = 0.3f * scale;
controller.center = new Vector3(0f, 0.95f * scale, 0f);
}
Transform CreatePart(string name, Transform parent, Vector3 localPos, Vector3 localScale, Color color, PrimitiveType shape = PrimitiveType.Cube)
{
GameObject part = GameObject.CreatePrimitive(shape);
part.name = name;
part.transform.SetParent(parent);
part.transform.localPosition = localPos;
part.transform.localScale = localScale;
part.transform.localRotation = Quaternion.identity;
// Remove collider from body parts (CharacterController handles collision)
Collider col = part.GetComponent<Collider>();
if (col != null) Destroy(col);
Renderer rend = part.GetComponent<Renderer>();
rend.material = new Material(Shader.Find("Standard"));
rend.material.color = color;
return part.transform;
}
// ─── Simple Limb Animation ───
void AnimateModel(bool isWalking)
{
if (bodyRoot == null) return;
if (isWalking)
{
animTimer += Time.deltaTime * moveSpeed * 1.5f;
float limbSwing = Mathf.Sin(animTimer) * 30f;
// Arms swing opposite to legs
if (leftArm != null)
leftArm.localRotation = Quaternion.Euler(limbSwing, 0f, 0f);
if (rightArm != null)
rightArm.localRotation = Quaternion.Euler(-limbSwing, 0f, 0f);
if (leftLeg != null)
leftLeg.localRotation = Quaternion.Euler(-limbSwing, 0f, 0f);
if (rightLeg != null)
rightLeg.localRotation = Quaternion.Euler(limbSwing, 0f, 0f);
// Slight torso bob
if (torsoTransform != null)
{
Vector3 torsoPos = torsoTransform.localPosition;
float scale = enemyType == EnemyType.Brute ? 1.3f : (enemyType == EnemyType.Runner ? 0.9f : 1f);
torsoPos.y = 1.15f * scale + Mathf.Abs(Mathf.Sin(animTimer * 2f)) * 0.03f;
torsoTransform.localPosition = torsoPos;
}
}
else
{
// Return to idle pose
animTimer = 0f;
if (leftArm != null)
leftArm.localRotation = Quaternion.Slerp(leftArm.localRotation, Quaternion.identity, Time.deltaTime * 5f);
if (rightArm != null)
rightArm.localRotation = Quaternion.Slerp(rightArm.localRotation, Quaternion.identity, Time.deltaTime * 5f);
if (leftLeg != null)
leftLeg.localRotation = Quaternion.Slerp(leftLeg.localRotation, Quaternion.identity, Time.deltaTime * 5f);
if (rightLeg != null)
rightLeg.localRotation = Quaternion.Slerp(rightLeg.localRotation, Quaternion.identity, Time.deltaTime * 5f);
}
}
IEnumerator PunchAnimation()
{
if (rightArm == null) yield break;
// Wind up
float timer = 0f;
float punchDuration = 0.15f;
while (timer < punchDuration)
{
timer += Time.deltaTime;
float t = timer / punchDuration;
rightArm.localRotation = Quaternion.Euler(Mathf.Lerp(0f, -90f, t), 0f, 0f);
yield return null;
}
// Snap back
timer = 0f;
while (timer < punchDuration)
{
timer += Time.deltaTime;
float t = timer / punchDuration;
rightArm.localRotation = Quaternion.Euler(Mathf.Lerp(-90f, 0f, t), 0f, 0f);
yield return null;
}
rightArm.localRotation = Quaternion.identity;
}
// ─── Gizmos ───
void OnDrawGizmosSelected()
{
// Chase range
Gizmos.color = new Color(1f, 0f, 0f, 0.15f);
Gizmos.DrawWireSphere(transform.position, chaseRange);
// Attack range
Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f);
Gizmos.DrawWireSphere(transform.position, attackRange);
// Patrol radius
Gizmos.color = new Color(0f, 0.5f, 1f, 0.15f);
Gizmos.DrawWireSphere(Application.isPlaying ? spawnPoint : transform.position, patrolRadius);
}
}