0% encontró este documento útil (0 votos)
88 vistas50 páginas

Creación de un FPS en Unity 3D

Este documento presenta el primer avance de un proyecto de videojuego en 3D realizado por estudiantes de la Facultad de Ciencias Físico Matemáticas de la Universidad Autónoma de Nuevo León. El juego consiste en un shooter en primera persona donde el jugador dispara enemigos en el espacio. Para este primer avance, crearon un nivel sencillo con un enemigo y un jefe. Explican brevemente el proceso de creación del juego en Unity y los scripts utilizados para el movimiento del jugador y otros componentes.

Cargado por

bkghoul
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
88 vistas50 páginas

Creación de un FPS en Unity 3D

Este documento presenta el primer avance de un proyecto de videojuego en 3D realizado por estudiantes de la Facultad de Ciencias Físico Matemáticas de la Universidad Autónoma de Nuevo León. El juego consiste en un shooter en primera persona donde el jugador dispara enemigos en el espacio. Para este primer avance, crearon un nivel sencillo con un enemigo y un jefe. Explican brevemente el proceso de creación del juego en Unity y los scripts utilizados para el movimiento del jugador y otros componentes.

Cargado por

bkghoul
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

UNIVERSIDAD AUTONOMA DE NUEVO LEON

FACULTAD DE CIENCIAS FISICO MATEMATICO

DOCUMENTACION DE PROYECTO

PRIMER AVANCE

Jazil Rebeca Guerrero González………1898899


Montserrat Ortega García………2076203
Esteban Alan Hinojosa González……….1966534
Miguel Cuellar Quintero………..1950524
María Yolanda Escudero Guzmán……..1656843
FPS Microgame (Juego de Shooting)

Para este proyecto, decidimos hacer un videojuego en 3D al que fuera sencillo


asignarle un haptico, en este caso, decidimos hacer un juego de shooting. UnityLearn
cuenta con muchas plantillas que pueden ser utilizadas por sus usuarios para
proyectos de práctica, así que después de investigar, decidimos utilizar el FPS
Microgame. UnityLearn cuenta con todos los materiales para crear un videojuego con
diferentes niveles y además proveen un manual de cómo hacerlo y programarlo. La
temática de este juego es sencilla, tu POV se encuentra en primera persona, te
encuentras en el espacio y es necesario dispararles a los enemigos para ganar. Para
este avance solo creamos un nivel sencillo con un enemigo y un boss para que el demo
sea rápido y fácil, ganas cuando matas a los enemigos, pierdes si ellos te matan a ti, y
siempre inicias en el mismo lugar.

PROCESO.

Para crear este videojuego, lo primero fue crear un proyecto vacio 3D en Unity, una vez
hecho esto, se descarga dentro del proyecto la carpeta con los componentes a utilizar
(Modelos 3D, estructuras, texturas, sonidos, etc.) Despues, el primer paso es
asignarles los prefabs (movimientos) a tu personaje. Al ser este un POV de primera
persona, no hay un personaje como tal, solamente un objeto transparente al que se le
asignan los movimientos, lo único visible es la pistola, que será nuestro haptico.
Después de tener al “personaje” en la carpeta de Prefabs, lo siguiente es asignarle un
Script (programar sus comandos en C#). Después de terminar de programarlo, hay que
asegurarse que el Prefab se haya guardado correctamente, después de esto, el modelo
quedara listo para jugar. Lo que sigue es crear el escenario donde el personaje se
moverá.

Esto se repite con los enemigos, los posicionamos en alguna parte del mapa y después
les asignamos su respectivo Script.
Este paquete de unity ya cuenta con escenas pre cargadas para que el usuario
simplemente la modifique a como desee y aprenda en el proceso, ya que también tiene
tutoriales incluidos. Para este avance, nosotros decidimos usar la default scene para
ver como funcionaria en el proyecto final, pero planeamos modificarla para el segundo
avance para que esta se vea diferente y existan más enemigos.

Nuestro juego es bastante sencillo pero divertido, y facilita la creación del haptico ya
que este será la pistola visualizada durante todo el juego.
El juego cuenta con dos pantallas, cuando se pierde y cuando se gana.
No importa si gane o pierda, el jugador siempre hará respawn en el mismo lugar de
inicio.
HAPTICO.

Al ser este un juego de shooting, nuestro haptico será la pistola que se muestra al
jugar. Para crearlo, utilizaremos el MPU6050 para el movimiento de la cámara, un
joystick para el movimiento del jugador y los disparos, leds decorativos, y un buzzer
que sonara cuando se le haga presión al joystick.
SCRIPTS.

Movimiento de Jugador.

using [Link];

using UnityEngine;

using [Link];

namespace [Link]

[RequireComponent(typeof(CharacterController), typeof(PlayerInputHandler),
typeof(AudioSource))]

public class PlayerCharacterController : MonoBehaviour

[Header("References")] [Tooltip("Reference to the main camera used for the


player")]

public Camera PlayerCamera;

[Tooltip("Audio source for footsteps, jump, etc...")]

public AudioSource AudioSource;

[Header("General")] [Tooltip("Force applied downward when in the air")]

public float GravityDownForce = 20f;

[Tooltip("Physic layers checked to consider the player grounded")]

public LayerMask GroundCheckLayers = -1;


[Tooltip("distance from the bottom of the character controller capsule to test
for grounded")]

public float GroundCheckDistance = 0.05f;

[Header("Movement")] [Tooltip("Max movement speed when grounded (when


not sprinting)")]

public float MaxSpeedOnGround = 10f;

[Tooltip(

"Sharpness for the movement when grounded, a low value will make the
player accelerate and decelerate slowly, a high value will do the opposite")]

public float MovementSharpnessOnGround = 15;

[Tooltip("Max movement speed when crouching")] [Range(0, 1)]

public float MaxSpeedCrouchedRatio = 0.5f;

[Tooltip("Max movement speed when not grounded")]

public float MaxSpeedInAir = 10f;

[Tooltip("Acceleration speed when in the air")]

public float AccelerationSpeedInAir = 25f;

[Tooltip("Multiplicator for the sprint speed (based on grounded speed)")]

public float SprintSpeedModifier = 2f;

[Tooltip("Height at which the player dies instantly when falling off the map")]
public float KillHeight = -50f;

[Header("Rotation")] [Tooltip("Rotation speed for moving the camera")]

public float RotationSpeed = 200f;

[Range(0.1f, 1f)] [Tooltip("Rotation speed multiplier when aiming")]

public float AimingRotationMultiplier = 0.4f;

[Header("Jump")] [Tooltip("Force applied upward when jumping")]

public float JumpForce = 9f;

[Header("Stance")] [Tooltip("Ratio (0-1) of the character height where the


camera will be at")]

public float CameraHeightRatio = 0.9f;

[Tooltip("Height of character when standing")]

public float CapsuleHeightStanding = 1.8f;

[Tooltip("Height of character when crouching")]

public float CapsuleHeightCrouching = 0.9f;

[Tooltip("Speed of crouching transitions")]

public float CrouchingSharpness = 10f;

[Header("Audio")] [Tooltip("Amount of footstep sounds played when moving


one meter")]
public float FootstepSfxFrequency = 1f;

[Tooltip("Amount of footstep sounds played when moving one meter while


sprinting")]

public float FootstepSfxFrequencyWhileSprinting = 1f;

[Tooltip("Sound played for footsteps")]

public AudioClip FootstepSfx;

[Tooltip("Sound played when jumping")] public AudioClip JumpSfx;

[Tooltip("Sound played when landing")] public AudioClip LandSfx;

[Tooltip("Sound played when taking damage froma fall")]

public AudioClip FallDamageSfx;

[Header("Fall Damage")]

[Tooltip("Whether the player will recieve damage when hitting the ground at
high speed")]

public bool RecievesFallDamage;

[Tooltip("Minimun fall speed for recieving fall damage")]

public float MinSpeedForFallDamage = 10f;

[Tooltip("Fall speed for recieving th emaximum amount of fall damage")]

public float MaxSpeedForFallDamage = 30f;


[Tooltip("Damage recieved when falling at the mimimum speed")]

public float FallDamageAtMinSpeed = 10f;

[Tooltip("Damage recieved when falling at the maximum speed")]

public float FallDamageAtMaxSpeed = 50f;

public UnityAction<bool> OnStanceChanged;

public Vector3 CharacterVelocity { get; set; }

public bool IsGrounded { get; private set; }

public bool HasJumpedThisFrame { get; private set; }

public bool IsDead { get; private set; }

public bool IsCrouching { get; private set; }

public float RotationMultiplier

get

if (m_WeaponsManager.IsAiming)

return AimingRotationMultiplier;

return 1f;

}
}

Health m_Health;

PlayerInputHandler m_InputHandler;

CharacterController m_Controller;

PlayerWeaponsManager m_WeaponsManager;

Actor m_Actor;

Vector3 m_GroundNormal;

Vector3 m_CharacterVelocity;

Vector3 m_LatestImpactSpeed;

float m_LastTimeJumped = 0f;

float m_CameraVerticalAngle = 0f;

float m_FootstepDistanceCounter;

float m_TargetCharacterHeight;

const float k_JumpGroundingPreventionTime = 0.2f;

const float k_GroundCheckDistanceInAir = 0.07f;

void Awake()

ActorsManager actorsManager = FindObjectOfType<ActorsManager>();

if (actorsManager != null)

[Link](gameObject);

}
void Start()

// fetch components on the same gameObject

m_Controller = GetComponent<CharacterController>();

[Link]<CharacterController,
PlayerCharacterController>(m_Controller,

this, gameObject);

m_InputHandler = GetComponent<PlayerInputHandler>();

[Link]<PlayerInputHandler,
PlayerCharacterController>(m_InputHandler,

this, gameObject);

m_WeaponsManager = GetComponent<PlayerWeaponsManager>();

[Link]<PlayerWeaponsManager,
PlayerCharacterController>(

m_WeaponsManager, this, gameObject);

m_Health = GetComponent<Health>();

[Link]<Health,
PlayerCharacterController>(m_Health, this, gameObject);

m_Actor = GetComponent<Actor>();

[Link]<Actor,
PlayerCharacterController>(m_Actor, this, gameObject);

m_Controller.enableOverlapRecovery = true;
m_Health.OnDie += OnDie;

// force the crouch state to false when starting

SetCrouchingState(false, true);

UpdateCharacterHeight(true);

void Update()

// check for Y kill

if (!IsDead && [Link].y < KillHeight)

m_Health.Kill();

HasJumpedThisFrame = false;

bool wasGrounded = IsGrounded;

GroundCheck();

// landing

if (IsGrounded && !wasGrounded)

// Fall damage
float fallSpeed = -[Link](CharacterVelocity.y,
m_LatestImpactSpeed.y);

float fallSpeedRatio = (fallSpeed - MinSpeedForFallDamage) /

(MaxSpeedForFallDamage - MinSpeedForFallDamage);

if (RecievesFallDamage && fallSpeedRatio > 0f)

float dmgFromFall = [Link](FallDamageAtMinSpeed,


FallDamageAtMaxSpeed, fallSpeedRatio);

m_Health.TakeDamage(dmgFromFall, null);

// fall damage SFX

[Link](FallDamageSfx);

else

// land SFX

[Link](LandSfx);

// crouching

if (m_InputHandler.GetCrouchInputDown())

SetCrouchingState(!IsCrouching, false);

}
UpdateCharacterHeight(false);

HandleCharacterMovement();

void OnDie()

IsDead = true;

// Tell the weapons manager to switch to a non-existing weapon in order to


lower the weapon

m_WeaponsManager.SwitchToWeaponIndex(-1, true);

[Link]([Link]);

void GroundCheck()

// Make sure that the ground check distance while already in air is very
small, to prevent suddenly snapping to ground

float chosenGroundCheckDistance =

IsGrounded ? (m_Controller.skinWidth + GroundCheckDistance) :


k_GroundCheckDistanceInAir;

// reset values before the ground check

IsGrounded = false;
m_GroundNormal = [Link];

// only try to detect ground if it's been a short amount of time since last
jump; otherwise we may snap to the ground instantly after we try jumping

if ([Link] >= m_LastTimeJumped + k_JumpGroundingPreventionTime)

// if we're grounded, collect info about the ground normal with a


downward capsule cast representing our character capsule

if ([Link](GetCapsuleBottomHemisphere(),
GetCapsuleTopHemisphere(m_Controller.height),

m_Controller.radius, [Link], out RaycastHit hit,


chosenGroundCheckDistance, GroundCheckLayers,

[Link]))

// storing the upward direction for the surface found

m_GroundNormal = [Link];

// Only consider this a valid ground hit if the ground normal goes in
the same direction as the character up

// and if the slope angle is lower than the character controller's limit

if ([Link]([Link], [Link]) > 0f &&

IsNormalUnderSlopeLimit(m_GroundNormal))

IsGrounded = true;

// handle snapping to the ground

if ([Link] > m_Controller.skinWidth)


{

m_Controller.Move([Link] * [Link]);

void HandleCharacterMovement()

// horizontal character rotation

// rotate the transform with the input speed around its local Y axis

[Link](

new Vector3(0f, (m_InputHandler.GetLookInputsHorizontal() *


RotationSpeed * RotationMultiplier),

0f), [Link]);

// vertical camera rotation

// add vertical inputs to the camera's vertical angle

m_CameraVerticalAngle += m_InputHandler.GetLookInputsVertical() *
RotationSpeed * RotationMultiplier;

// limit the camera's vertical angle to min/max


m_CameraVerticalAngle = [Link](m_CameraVerticalAngle, -89f,
89f);

// apply the vertical angle as a local rotation to the camera transform


along its right axis (makes it pivot up and down)

[Link] = new
Vector3(m_CameraVerticalAngle, 0, 0);

// character movement handling

bool isSprinting = m_InputHandler.GetSprintInputHeld();

if (isSprinting)

isSprinting = SetCrouchingState(false, false);

float speedModifier = isSprinting ? SprintSpeedModifier : 1f;

// converts move input to a worldspace vector based on our character's


transform orientation

Vector3 worldspaceMoveInput =
[Link](m_InputHandler.GetMoveInput());

// handle grounded movement

if (IsGrounded)

{
// calculate the desired velocity from inputs, max speed, and current
slope

Vector3 targetVelocity = worldspaceMoveInput * MaxSpeedOnGround


* speedModifier;

// reduce speed if crouching by crouch speed ratio

if (IsCrouching)

targetVelocity *= MaxSpeedCrouchedRatio;

targetVelocity =
GetDirectionReorientedOnSlope([Link], m_GroundNormal) *

[Link];

// smoothly interpolate between our current velocity and the target


velocity based on acceleration speed

CharacterVelocity = [Link](CharacterVelocity, targetVelocity,

MovementSharpnessOnGround * [Link]);

// jumping

if (IsGrounded && m_InputHandler.GetJumpInputDown())

// force the crouch state to false

if (SetCrouchingState(false, false))

// start by canceling out the vertical component of our velocity

CharacterVelocity = new Vector3(CharacterVelocity.x, 0f,


CharacterVelocity.z);

// then, add the jumpSpeed value upwards


CharacterVelocity += [Link] * JumpForce;

// play sound

[Link](JumpSfx);

// remember last time we jumped because we need to prevent


snapping to ground for a short time

m_LastTimeJumped = [Link];

HasJumpedThisFrame = true;

// Force grounding to false

IsGrounded = false;

m_GroundNormal = [Link];

// footsteps sound

float chosenFootstepSfxFrequency =

(isSprinting ? FootstepSfxFrequencyWhileSprinting :
FootstepSfxFrequency);

if (m_FootstepDistanceCounter >= 1f / chosenFootstepSfxFrequency)

m_FootstepDistanceCounter = 0f;

[Link](FootstepSfx);

}
// keep track of distance traveled for footsteps sound

m_FootstepDistanceCounter += [Link] *
[Link];

// handle air movement

else

// add air acceleration

CharacterVelocity += worldspaceMoveInput * AccelerationSpeedInAir


* [Link];

// limit air speed to a maximum, but only horizontally

float verticalVelocity = CharacterVelocity.y;

Vector3 horizontalVelocity =
[Link](CharacterVelocity, [Link]);

horizontalVelocity = [Link](horizontalVelocity,
MaxSpeedInAir * speedModifier);

CharacterVelocity = horizontalVelocity + ([Link] *


verticalVelocity);

// apply the gravity to the velocity

CharacterVelocity += [Link] * GravityDownForce *


[Link];

// apply the final calculated velocity value as a character movement


Vector3 capsuleBottomBeforeMove = GetCapsuleBottomHemisphere();

Vector3 capsuleTopBeforeMove =
GetCapsuleTopHemisphere(m_Controller.height);

m_Controller.Move(CharacterVelocity * [Link]);

// detect obstructions to adjust velocity accordingly

m_LatestImpactSpeed = [Link];

if ([Link](capsuleBottomBeforeMove,
capsuleTopBeforeMove, m_Controller.radius,

[Link], out RaycastHit hit,


[Link] * [Link], -1,

[Link]))

// We remember the last impact speed because the fall damage logic
might need it

m_LatestImpactSpeed = CharacterVelocity;

CharacterVelocity = [Link](CharacterVelocity,
[Link]);

// Returns true if the slope angle represented by the given normal is under
the slope angle limit of the character controller

bool IsNormalUnderSlopeLimit(Vector3 normal)

return [Link]([Link], normal) <= m_Controller.slopeLimit;


}

// Gets the center point of the bottom hemisphere of the character controller
capsule

Vector3 GetCapsuleBottomHemisphere()

return [Link] + ([Link] * m_Controller.radius);

// Gets the center point of the top hemisphere of the character controller
capsule

Vector3 GetCapsuleTopHemisphere(float atHeight)

return [Link] + ([Link] * (atHeight -


m_Controller.radius));

// Gets a reoriented direction that is tangent to a given slope

public Vector3 GetDirectionReorientedOnSlope(Vector3 direction, Vector3


slopeNormal)

Vector3 directionRight = [Link](direction, [Link]);

return [Link](slopeNormal, directionRight).normalized;

void UpdateCharacterHeight(bool force)

{
// Update height instantly

if (force)

m_Controller.height = m_TargetCharacterHeight;

m_Controller.center = [Link] * m_Controller.height * 0.5f;

[Link] = [Link] *
m_TargetCharacterHeight * CameraHeightRatio;

m_Actor.[Link] = m_Controller.center;

// Update smooth height

else if (m_Controller.height != m_TargetCharacterHeight)

// resize the capsule and adjust camera position

m_Controller.height = [Link](m_Controller.height,
m_TargetCharacterHeight,

CrouchingSharpness * [Link]);

m_Controller.center = [Link] * m_Controller.height * 0.5f;

[Link] =
[Link]([Link],

[Link] * m_TargetCharacterHeight * CameraHeightRatio,


CrouchingSharpness * [Link]);

m_Actor.[Link] = m_Controller.center;

// returns false if there was an obstruction

bool SetCrouchingState(bool crouched, bool ignoreObstructions)


{

// set appropriate heights

if (crouched)

m_TargetCharacterHeight = CapsuleHeightCrouching;

else

// Detect obstructions

if (!ignoreObstructions)

Collider[] standingOverlaps = [Link](

GetCapsuleBottomHemisphere(),

GetCapsuleTopHemisphere(CapsuleHeightStanding),

m_Controller.radius,

-1,

[Link]);

foreach (Collider c in standingOverlaps)

if (c != m_Controller)

return false;

}
m_TargetCharacterHeight = CapsuleHeightStanding;

if (OnStanceChanged != null)

[Link](crouched);

IsCrouching = crouched;

return true;

}
ENEMIGOS.

using [Link];

using [Link];

using UnityEngine;

using [Link];

using [Link];

namespace [Link]

[RequireComponent(typeof(Health), typeof(Actor), typeof(NavMeshAgent))]

public class EnemyController : MonoBehaviour

[[Link]]

public struct RendererIndexData

public Renderer Renderer;

public int MaterialIndex;

public RendererIndexData(Renderer renderer, int index)

Renderer = renderer;

MaterialIndex = index;

}
[Header("Parameters")]

[Tooltip("The Y height at which the enemy will be automatically killed (if it


falls off of the level)")]

public float SelfDestructYHeight = -20f;

[Tooltip("The distance at which the enemy considers that it has reached its
current path destination point")]

public float PathReachingRadius = 2f;

[Tooltip("The speed at which the enemy rotates")]

public float OrientationSpeed = 10f;

[Tooltip("Delay after death where the GameObject is destroyed (to allow for
animation)")]

public float DeathDuration = 0f;

[Header("Weapons Parameters")] [Tooltip("Allow weapon swapping for this


enemy")]

public bool SwapToNextWeapon = false;

[Tooltip("Time delay between a weapon swap and the next attack")]

public float DelayAfterWeaponSwap = 0f;

[Header("Eye color")] [Tooltip("Material for the eye color")]

public Material EyeColorMaterial;


[Tooltip("The default color of the bot's eye")] [ColorUsageAttribute(true,
true)]

public Color DefaultEyeColor;

[Tooltip("The attack color of the bot's eye")] [ColorUsageAttribute(true, true)]

public Color AttackEyeColor;

[Header("Flash on hit")] [Tooltip("The material used for the body of the


hoverbot")]

public Material BodyMaterial;

[Tooltip("The gradient representing the color of the flash on hit")]


[GradientUsageAttribute(true)]

public Gradient OnHitBodyGradient;

[Tooltip("The duration of the flash on hit")]

public float FlashOnHitDuration = 0.5f;

[Header("Sounds")] [Tooltip("Sound played when recieving damages")]

public AudioClip DamageTick;

[Header("VFX")] [Tooltip("The VFX prefab spawned when the enemy dies")]

public GameObject DeathVfx;

[Tooltip("The point at which the death VFX is spawned")]

public Transform DeathVfxSpawnPoint;


[Header("Loot")] [Tooltip("The object this enemy can drop when dying")]

public GameObject LootPrefab;

[Tooltip("The chance the object has to drop")] [Range(0, 1)]

public float DropRate = 1f;

[Header("Debug Display")] [Tooltip("Color of the sphere gizmo representing


the path reaching range")]

public Color PathReachingRangeColor = [Link];

[Tooltip("Color of the sphere gizmo representing the attack range")]

public Color AttackRangeColor = [Link];

[Tooltip("Color of the sphere gizmo representing the detection range")]

public Color DetectionRangeColor = [Link];

public UnityAction onAttack;

public UnityAction onDetectedTarget;

public UnityAction onLostTarget;

public UnityAction onDamaged;

List<RendererIndexData> m_BodyRenderers = new


List<RendererIndexData>();

MaterialPropertyBlock m_BodyFlashMaterialPropertyBlock;

float m_LastTimeDamaged = [Link];


RendererIndexData m_EyeRendererData;

MaterialPropertyBlock m_EyeColorMaterialPropertyBlock;

public PatrolPath PatrolPath { get; set; }

public GameObject KnownDetectedTarget =>


[Link];

public bool IsTargetInAttackRange =>


[Link];

public bool IsSeeingTarget => [Link];

public bool HadKnownTarget => [Link];

public NavMeshAgent NavMeshAgent { get; private set; }

public DetectionModule DetectionModule { get; private set; }

int m_PathDestinationNodeIndex;

EnemyManager m_EnemyManager;

ActorsManager m_ActorsManager;

Health m_Health;

Actor m_Actor;

Collider[] m_SelfColliders;

GameFlowManager m_GameFlowManager;

bool m_WasDamagedThisFrame;

float m_LastTimeWeaponSwapped = [Link];

int m_CurrentWeaponIndex;

WeaponController m_CurrentWeapon;

WeaponController[] m_Weapons;
NavigationModule m_NavigationModule;

void Start()

m_EnemyManager = FindObjectOfType<EnemyManager>();

[Link]<EnemyManager,
EnemyController>(m_EnemyManager, this);

m_ActorsManager = FindObjectOfType<ActorsManager>();

[Link]<ActorsManager,
EnemyController>(m_ActorsManager, this);

m_EnemyManager.RegisterEnemy(this);

m_Health = GetComponent<Health>();

[Link]<Health,
EnemyController>(m_Health, this, gameObject);

m_Actor = GetComponent<Actor>();

[Link]<Actor,
EnemyController>(m_Actor, this, gameObject);

NavMeshAgent = GetComponent<NavMeshAgent>();

m_SelfColliders = GetComponentsInChildren<Collider>();

m_GameFlowManager = FindObjectOfType<GameFlowManager>();
[Link]<GameFlowManager,
EnemyController>(m_GameFlowManager, this);

// Subscribe to damage & death actions

m_Health.OnDie += OnDie;

m_Health.OnDamaged += OnDamaged;

// Find and initialize all weapons

FindAndInitializeAllWeapons();

var weapon = GetCurrentWeapon();

[Link](true);

var detectionModules = GetComponentsInChildren<DetectionModule>();

[Link]<DetectionModule,
EnemyController>([Link], this,

gameObject);

[Link]<DetectionModule,
EnemyController>([Link],

this, gameObject);

// Initialize detection module

DetectionModule = detectionModules[0];

[Link] += OnDetectedTarget;

[Link] += OnLostTarget;

onAttack += [Link];

var navigationModules = GetComponentsInChildren<NavigationModule>();


[Link]<DetectionModule,
EnemyController>([Link],

this, gameObject);

// Override navmesh agent data

if ([Link] > 0)

m_NavigationModule = navigationModules[0];

[Link] = m_NavigationModule.MoveSpeed;

[Link] = m_NavigationModule.AngularSpeed;

[Link] = m_NavigationModule.Acceleration;

foreach (var renderer in GetComponentsInChildren<Renderer>(true))

for (int i = 0; i < [Link]; i++)

if ([Link][i] == EyeColorMaterial)

m_EyeRendererData = new RendererIndexData(renderer, i);

if ([Link][i] == BodyMaterial)

m_BodyRenderers.Add(new RendererIndexData(renderer, i));

}
}

m_BodyFlashMaterialPropertyBlock = new MaterialPropertyBlock();

// Check if we have an eye renderer for this enemy

if (m_EyeRendererData.Renderer != null)

m_EyeColorMaterialPropertyBlock = new MaterialPropertyBlock();

m_EyeColorMaterialPropertyBlock.SetColor("_EmissionColor",
DefaultEyeColor);

m_EyeRendererData.[Link](m_EyeColorMaterialPropertyBlo
ck,

m_EyeRendererData.MaterialIndex);

void Update()

EnsureIsWithinLevelBounds();

[Link](m_Actor, m_SelfColliders);

Color currentColor = [Link](([Link] -


m_LastTimeDamaged) / FlashOnHitDuration);
m_BodyFlashMaterialPropertyBlock.SetColor("_EmissionColor",
currentColor);

foreach (var data in m_BodyRenderers)

[Link](m_BodyFlashMaterialPropertyBlock,
[Link]);

m_WasDamagedThisFrame = false;

void EnsureIsWithinLevelBounds()

// at every frame, this tests for conditions to kill the enemy

if ([Link].y < SelfDestructYHeight)

Destroy(gameObject);

return;

void OnLostTarget()

[Link]();

// Set the eye attack color and property block if the eye renderer is set
if (m_EyeRendererData.Renderer != null)

m_EyeColorMaterialPropertyBlock.SetColor("_EmissionColor",
DefaultEyeColor);

m_EyeRendererData.[Link](m_EyeColorMaterialPropertyBlo
ck,

m_EyeRendererData.MaterialIndex);

void OnDetectedTarget()

[Link]();

// Set the eye default color and property block if the eye renderer is set

if (m_EyeRendererData.Renderer != null)

m_EyeColorMaterialPropertyBlock.SetColor("_EmissionColor",
AttackEyeColor);

m_EyeRendererData.[Link](m_EyeColorMaterialPropertyBlo
ck,

m_EyeRendererData.MaterialIndex);

}
public void OrientTowards(Vector3 lookPosition)

Vector3 lookDirection = [Link](lookPosition -


[Link], [Link]).normalized;

if ([Link] != 0f)

Quaternion targetRotation = [Link](lookDirection);

[Link] =

[Link]([Link], targetRotation, [Link] *


OrientationSpeed);

bool IsPathValid()

return PatrolPath && [Link] > 0;

public void ResetPathDestination()

m_PathDestinationNodeIndex = 0;

public void SetPathDestinationToClosestNode()

if (IsPathValid())
{

int closestPathNodeIndex = 0;

for (int i = 0; i < [Link]; i++)

float distanceToPathNode =
[Link]([Link], i);

if (distanceToPathNode <
[Link]([Link], closestPathNodeIndex))

closestPathNodeIndex = i;

m_PathDestinationNodeIndex = closestPathNodeIndex;

else

m_PathDestinationNodeIndex = 0;

public Vector3 GetDestinationOnPath()

if (IsPathValid())

{
return
[Link](m_PathDestinationNodeIndex);

else

return [Link];

public void SetNavDestination(Vector3 destination)

if (NavMeshAgent)

[Link](destination);

public void UpdatePathDestination(bool inverseOrder = false)

if (IsPathValid())

// Check if reached the path destination

if (([Link] - GetDestinationOnPath()).magnitude <=


PathReachingRadius)

// increment path destination index


m_PathDestinationNodeIndex =

inverseOrder ? (m_PathDestinationNodeIndex - 1) :
(m_PathDestinationNodeIndex + 1);

if (m_PathDestinationNodeIndex < 0)

m_PathDestinationNodeIndex += [Link];

if (m_PathDestinationNodeIndex >= [Link])

m_PathDestinationNodeIndex -= [Link];

void OnDamaged(float damage, GameObject damageSource)

// test if the damage source is the player

if (damageSource && ![Link]<EnemyController>())

// pursue the player

[Link](damageSource);

onDamaged?.Invoke();
m_LastTimeDamaged = [Link];

// play the damage tick sound

if (DamageTick && !m_WasDamagedThisFrame)

[Link](DamageTick, [Link],
[Link], 0f);

m_WasDamagedThisFrame = true;

void OnDie()

// spawn a particle system when dying

var vfx = Instantiate(DeathVfx, [Link],


[Link]);

Destroy(vfx, 5f);

// tells the game flow manager to handle the enemy destuction

m_EnemyManager.UnregisterEnemy(this);

// loot an object

if (TryDropItem())

Instantiate(LootPrefab, [Link], [Link]);

}
// this will call the OnDestroy function

Destroy(gameObject, DeathDuration);

void OnDrawGizmosSelected()

// Path reaching range

[Link] = PathReachingRangeColor;

[Link]([Link], PathReachingRadius);

if (DetectionModule != null)

// Detection range

[Link] = DetectionRangeColor;

[Link]([Link],
[Link]);

// Attack range

[Link] = AttackRangeColor;

[Link]([Link],
[Link]);

public void OrientWeaponsTowards(Vector3 lookPosition)


{

for (int i = 0; i < m_Weapons.Length; i++)

// orient weapon towards player

Vector3 weaponForward = (lookPosition -


m_Weapons[i].[Link]).normalized;

m_Weapons[i].[Link] = weaponForward;

public bool TryAtack(Vector3 enemyPosition)

if (m_GameFlowManager.GameIsEnding)

return false;

OrientWeaponsTowards(enemyPosition);

if ((m_LastTimeWeaponSwapped + DelayAfterWeaponSwap) >= [Link])

return false;

// Shoot the weapon

bool didFire = GetCurrentWeapon().HandleShootInputs(false, true, false);

if (didFire && onAttack != null)

{
[Link]();

if (SwapToNextWeapon && m_Weapons.Length > 1)

int nextWeaponIndex = (m_CurrentWeaponIndex + 1) %


m_Weapons.Length;

SetCurrentWeapon(nextWeaponIndex);

return didFire;

public bool TryDropItem()

if (DropRate == 0 || LootPrefab == null)

return false;

else if (DropRate == 1)

return true;

else

return ([Link] <= DropRate);

void FindAndInitializeAllWeapons()

{
// Check if we already found and initialized the weapons

if (m_Weapons == null)

m_Weapons = GetComponentsInChildren<WeaponController>();

[Link]<WeaponController,
EnemyController>(m_Weapons.Length, this,

gameObject);

for (int i = 0; i < m_Weapons.Length; i++)

m_Weapons[i].Owner = gameObject;

public WeaponController GetCurrentWeapon()

FindAndInitializeAllWeapons();

// Check if no weapon is currently selected

if (m_CurrentWeapon == null)

// Set the first weapon of the weapons list as the current weapon

SetCurrentWeapon(0);

}
[Link]<WeaponController,
EnemyController>(m_CurrentWeapon, this,

gameObject);

return m_CurrentWeapon;

void SetCurrentWeapon(int index)

m_CurrentWeaponIndex = index;

m_CurrentWeapon = m_Weapons[m_CurrentWeaponIndex];

if (SwapToNextWeapon)

m_LastTimeWeaponSwapped = [Link];

else

m_LastTimeWeaponSwapped = [Link];

}
RECURSOS.

[Link]

También podría gustarte