This is a third person "shooter" game where you play as a bodybuilder defending Venice Beach from an alien invasion. The player uses correctly timed poses and flexes to deflect enemy projectiles and eliminate threat.
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
[RequireComponent(typeof(CapsuleCollider))]
public abstract class BulletDeflector : MonoBehaviour {
[SerializeField]
private PlayerState playerState;
[SerializeField]
private BooleanVariable disableInput;
[SerializeField]
private FloatVariable stamina;
[SerializeField]
public EnumVariable bulletTypeToDeflect;
[SerializeField]
private FloatVariable mainTrigger;
[SerializeField]
private FloatVariable otherTrigger;
[SerializeField]
private BooleanVariable active;
[SerializeField]
private BooleanVariable otherTriggerActiveState;
[SerializeField]
private DeflectorProperties deflectorProperties;
[SerializeField]
private GameEvent OnPerfectDeflection;
[SerializeField]
private GameEvent OnPoseStarted;
[SerializeField]
private GameEvent addScore;
[SerializeField]
private float minScoreAddition = 100f;
[SerializeField]
private float maxScoreAddition = 250f;
private Camera mainCamera;
private CapsuleCollider coll;
private float maxRadius;
private bool mainTriggerPreviousState = false;
private bool otherTriggerPreviousState = false;
private float startDeflectionTimestamp = 0f;
private void Awake()
{
mainCamera = Camera.main;
coll = GetComponent<CapsuleCollider>();
maxRadius = coll.radius;
playerState.airDeflect = false;
}
private void FixedUpdate() {
if (disableInput.Value || playerState.isPlayerDead) { active.Value = false; return; };
active.Value = playerState.sliding == false &&
playerState.throwingObject == false &&
playerState.throwingGrenade == false &&
playerState.meleeing == false &&
stamina.Value > 0f &&
((mainTrigger.Value != 0 && otherTrigger.Value == 0) || (mainTrigger.Value != 0 && otherTrigger.Value != 0 && mainTriggerPreviousState)) &&
otherTriggerActiveState.Value == false && otherTriggerPreviousState == false;
// First frame of deflection
if (active.Value && mainTriggerPreviousState == false) {
OnPoseStarted.Raise(this);
startDeflectionTimestamp = Time.time;
if (playerState.jumping || playerState.grounded == false) playerState.airDeflect = true;
}
// The frame the player let go off the trigger
if (active.Value == false && mainTriggerPreviousState == true) {
playerState.airDeflect = false;
}
coll.enabled = active.Value;
playerState.deflecting = active.Value || otherTriggerActiveState.Value;
mainTriggerPreviousState = active.Value;
otherTriggerPreviousState = otherTriggerActiveState.Value;
}
private void EvaluateDeflection(Bullet bullet)
{
if (Time.time - deflectorProperties.fixedTimeWindow > startDeflectionTimestamp) {
bullet.DeflectRandomly();
return;
}
float distance = Vector3.Magnitude(bullet.transform.position - transform.position);
if (distance <= deflectorProperties.maxDistanceToPerfectDeflection) {
distance = Mathf.Clamp(distance, deflectorProperties.minDistanceToPerfectDeflection, deflectorProperties.maxDistanceToPerfectDeflection);
float multiplier = 1f -distance.Normalize(deflectorProperties.minDistanceToPerfectDeflection, deflectorProperties.maxDistanceToPerfectDeflection);
bullet.DeflectPerfeclty(multiplier);
OnPerfectDeflection.Raise(multiplier);
addScore.Raise(multiplier.Denormalize(minScoreAddition, maxScoreAddition));
} else {
bullet.DeflectRandomly();
}
}
private void OnTriggerEnter(Collider other) {
Bullet bullet = other.gameObject.GetComponent<Bullet>();
if (bullet != null && bullet.bulletType == bulletTypeToDeflect && bullet.bouncedByPlayer == false) {
EvaluateDeflection(bullet);
}
}
}
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
[RequireComponent(typeof(LineRenderer))]
public class LaunchArch : MonoBehaviour
{
[SerializeField]
private GameObject explosionIndicator;
[SerializeField]
private BooleanVariable showArch;
[SerializeField]
private FloatVariable throwingForce;
[SerializeField]
private LayerMask layerMask = -1;
private GameObject indicatorInstance = null;
public int maxPositionCount = 1024;
private LineRenderer lr;
private void Awake()
{
lr = GetComponent<LineRenderer>();
indicatorInstance = Instantiate(explosionIndicator, transform.position, Quaternion.identity);
indicatorInstance.SetActive(false);
}
private void Update()
{
if (showArch.Value == false)
{
lr.positionCount = 0;
if (indicatorInstance.activeInHierarchy) indicatorInstance.SetActive(false);
}
else
{
DrawArch();
}
}
private void DrawArch()
{
// Initialize Values
float velocity = throwingForce.Value;
Vector3 currentVelocity = transform.forward * velocity;
Vector3 currentPosition = transform.position;
lr.positionCount = maxPositionCount;
int index = 0;
bool keepDrawingArch = true;
while (index < maxPositionCount || keepDrawingArch == false)
{
// Draw the current line renderer position
lr.SetPosition(index, currentPosition);
// Calculate the next position
float time = Time.deltaTime;
Vector3 nextVelocity = currentVelocity + Physics.gravity * time;
Vector3 nextPosition = currentPosition + nextVelocity * time;
// Check if it hits something and start
RaycastHit hit;
Vector3 nextPositionDirection = nextPosition - currentPosition;
if (Physics.Raycast(currentPosition, nextPositionDirection, out hit, nextPositionDirection.magnitude, layerMask))
{
lr.positionCount = index + 2;
lr.SetPosition(index + 1, hit.point);
if (indicatorInstance != null)
{
indicatorInstance.SetActive(true);
indicatorInstance.transform.forward = hit.normal;
indicatorInstance.transform.position = hit.point - transform.forward * 0.25f;
};
break;
}
else
{
indicatorInstance.SetActive(false);
}
currentPosition = nextPosition;
currentVelocity = nextVelocity;
index++;
}
}
}
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
[System.Serializable]
public class Response : UnityEvent<object> {}
public class GameEventListener : MonoBehaviour {
public GameEvent gameEvent;
public Response response;
private void OnEnable() {
gameEvent.RegisterListener(this);
}
private void OnDisable() {
gameEvent.UnregisterListener(this);
}
public void OnEventRaised(object data) {
response.Invoke(data);
}
}
[CreateAssetMenu(fileName="NewGameEvent", menuName="SunsOutGunsOut/GameEvent")]
public class GameEvent : ScriptableObject {
private List<GameEventListener> listeners = new List<GameEventListener>();
public void Raise(object data) {
// Go backwards in case we delete one listener
for (int i = listeners.Count -1; i >= 0; i--) {
listeners[i].OnEventRaised(data);
}
}
public void RegisterListener(GameEventListener listener) {
listeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener) {
listeners.Remove(listener);
}
}
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
public class BulletIndicatorManager : MonoBehaviour {
[SerializeField]
private DeflectorProperties deflectorProperties;
[SerializeField]
private BulletIndicatorProperties indicatorProperties;
[SerializeField]
private TransformVariable player;
[SerializeField]
private BulletTracker blueBulletTracker;
[SerializeField]
private BulletTracker redBulletTracker;
[SerializeField]
private EnumVariable blueBulletType;
[SerializeField]
private EnumVariable redBulletType;
[SerializeField]
private PoolableObject redIndicatorPrefab;
[SerializeField]
private PoolableObject blueIndicatorPrefab;
[SerializeField]
private int poolSize = 5;
private void Awake() {
blueBulletTracker.bullets = new List<Bullet>();
redBulletTracker.bullets = new List<Bullet>();
}
private void Start() {
PoolManager.CreatePool(redIndicatorPrefab, poolSize);
PoolManager.CreatePool(blueIndicatorPrefab, poolSize);
}
private void Update() {
int blueBulletsCount = blueBulletTracker.bullets.Count;
int redBulletsCount = redBulletTracker.bullets.Count;
if (blueBulletsCount == 0 && redBulletsCount == 0) return;
if (blueBulletsCount > 0) {
foreach (Bullet bullet in blueBulletTracker.bullets) {
if (bullet != null) {
DetermineVisibility(bullet, blueIndicatorPrefab);
}
}
}
if (redBulletsCount > 0) {
foreach (Bullet bullet in redBulletTracker.bullets) {
DetermineVisibility(bullet, redIndicatorPrefab);
}
}
}
private void DetermineVisibility(Bullet bullet, PoolableObject pObject) {
Vector3 direction = bullet.transform.position - player.Value.transform.position;
direction.y = 0;
if (direction.magnitude <= indicatorProperties.minDistanceRange) {
var clone = PoolManager.GetObjectFromPool(pObject);
BulletIndicator indicator = clone.GetComponent<BulletIndicator>();
indicator.transform.position = transform.position;
indicator.transform.forward = transform.up;
indicator.SetScale(direction.magnitude);
indicator.gameObject.SetActive(true);
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.white;
Gizmos.DrawLine(transform.position, transform.position + transform.forward * deflectorProperties.maxDistanceToPerfectDeflection);
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + transform.forward * deflectorProperties.minDistanceToPerfectDeflection);
}
}