Open-sourced some code

This commit is contained in:
2019-03-08 09:54:14 +11:00
commit 645272872c
142 changed files with 3028 additions and 0 deletions
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
# Last Ember - Open Source
In this repository are the parts of the game Last Ember that I created myself. This includes the C# scripts, and a few placeholder sprites. Everything else is too tied up to release as open source.
This game was written in C# for Unity 2018.3.0f2.
I've included a build of the game that shows off almost everything created so far.
You can run by double-tapping a direction, jump, wall jump, look up and crouch. There were going to be other moves, but we didn't get around to implementing them.
## Libraries and Tools Used
* Carbon Input - https://assetstore.unity.com/packages/tools/input-management/unified-input-manager-56980
* SpriterDotNet - https://github.com/loodakrawa/SpriterDotNet
## Credits
* Kayne Ruse - Programming, Management
* Evan Hartshorn - Art, Game Design
* Shy Monster (Luis Paez, Hayden Blades) - Audio, Music
Special thanks to Feldi - sorry we didn't get to work with you!
## $5 Patrons
* Seth Robinson
## Contact
If you have any questions, you're welcome to ask here:
kayneruse@gmail.com
https://discord.gg/FQmz8TN
## Copyright
Copyright (c) 2019 KR Game Studios
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+212
View File
@@ -0,0 +1,212 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioController : MonoBehaviour {
//public structures
public enum Mode {
NONE,
ONCE,
LOOP,
JUMP
}
public struct AudioContainer {
public AudioSource source;
public Mode mode;
public float jumpStart;
public float jumpEnd;
}
//internals
Dictionary<string, AudioContainer> audioDictionary = new Dictionary<string, AudioContainer>();
static bool initialized = false;
//monobehaviour methods
void Start() {
if (initialized) {
Destroy(gameObject);
}
initialized = true;
DontDestroyOnLoad(gameObject);
}
void Update() {
foreach(KeyValuePair<string, AudioContainer> iter in audioDictionary) {
//handle the jump points
if (iter.Value.mode == Mode.JUMP && iter.Value.jumpStart >= 0f && iter.Value.jumpEnd > 0f) {
if (iter.Value.source.time >= iter.Value.jumpEnd) {
iter.Value.source.time = iter.Value.jumpStart;
}
}
}
}
void OnDestroy() {
foreach(KeyValuePair<string, AudioContainer> iter in audioDictionary) {
Resources.UnloadAsset(iter.Value.source.clip);
Destroy(iter.Value.source);
}
}
//public access members
public void Load(string name, string filename) {
AudioContainer container = new AudioContainer();
container.source = gameObject.AddComponent(typeof(AudioSource)) as AudioSource;
container.source.clip = Resources.Load<AudioClip>(filename) as AudioClip;
container.source.volume = 0f;
container.mode = Mode.NONE;
audioDictionary[name] = container;
}
public bool Unload(string name) {
if (!audioDictionary.ContainsKey(name)) {
return false;
}
AudioContainer container = audioDictionary[name];
Resources.UnloadAsset(container.source.clip);
Destroy(container.source);
audioDictionary.Remove(name);
return true;
}
//controls
public void Play(string name, Mode mode = Mode.ONCE, float jumpStart = -1f, float jumpEnd = -1f) {
AudioContainer container = audioDictionary[name];
container.source.Play();
container.source.loop = mode == Mode.LOOP;
container.source.volume = 1f;
container.mode = mode;
container.jumpStart = jumpStart;
container.jumpEnd = jumpEnd;
audioDictionary[name] = container;
}
public void Pause(string name) {
AudioContainer container = audioDictionary[name];
container.source.Pause();
}
public void Unpause(string name, Mode mode = Mode.ONCE, float jumpStart = -1f, float jumpEnd = -1f) {
AudioContainer container = audioDictionary[name];
if (container.source.isPlaying) {
container.source.UnPause();
} else {
Play(name, mode, jumpStart, jumpEnd);
}
}
public void Stop(string name) {
AudioContainer container = audioDictionary[name];
container.source.Stop();
container.mode = Mode.NONE;
audioDictionary[name] = container;
}
public void StopAll() {
List<string> names = new List<string>();
foreach(KeyValuePair<string, AudioContainer> iter in audioDictionary) {
names.Add(iter.Key);
}
foreach(string name in names) {
Stop(name);
}
}
//fade controls
public void FadeIn(string name, float seconds) {
StartCoroutine(FadeInCallback(audioDictionary[name].source, 1f/seconds));
}
IEnumerator FadeInCallback(AudioSource source, float amountPerSecond) {
source.volume = 0;
while (source.volume < 1f) {
yield return new WaitForSeconds(0.1f);
source.volume += amountPerSecond / 10f;
}
}
public void FadeOut(string name, float seconds) {
StartCoroutine(FadeOutCallback(audioDictionary[name].source, 1f/seconds));
}
IEnumerator FadeOutCallback(AudioSource source, float amountPerSecond) {
while (source.volume > 0f) {
yield return new WaitForSeconds(0.1f);
source.volume -= amountPerSecond / 10f;
}
}
//hybrid controls
public void PlayFadeIn(string name, float seconds, Mode mode = Mode.ONCE, float jumpStart = -1f, float jumpEnd = -1f) {
FadeIn(name, seconds);
Play(name, mode, jumpStart, jumpEnd);
}
public void PauseFadeOut(string name, float seconds) {
FadeOut(name, seconds);
StartCoroutine(PauseFadeOutCallback(name, seconds));
}
public void PauseFadeOutAll(float seconds, List<string> exclude = null) {
foreach(KeyValuePair<string, AudioContainer> iter in audioDictionary) {
if (exclude != null && exclude.Contains(iter.Key)) {
continue;
}
FadeOut(iter.Key, seconds);
StartCoroutine(PauseFadeOutCallback(iter.Key, seconds));
}
}
IEnumerator PauseFadeOutCallback(string name, float seconds) {
yield return new WaitForSeconds(seconds);
Pause(name);
}
public void UnpauseFadeIn(string name, float seconds, Mode mode = Mode.ONCE, float jumpStart = -1f, float jumpEnd = -1f) {
Unpause(name, mode, jumpStart, jumpEnd);
FadeIn(name, seconds);
}
public void StopFadeOut(string name, float seconds) {
FadeOut(name, seconds);
StartCoroutine(StopFadeOutCallback(name, seconds));
}
public void StopFadeOutAll(float seconds, List<string> exclude = null) {
foreach(KeyValuePair<string, AudioContainer> iter in audioDictionary) {
if (exclude != null && exclude.Contains(iter.Key)) {
continue;
}
FadeOut(iter.Key, seconds);
StartCoroutine(StopFadeOutCallback(iter.Key, seconds));
}
}
IEnumerator StopFadeOutCallback(string name, float seconds) {
yield return new WaitForSeconds(seconds);
Stop(name);
}
//status
public bool GetPlaying(string name) {
return audioDictionary[name].source.isPlaying;
}
public Mode GetMode(string name) {
return audioDictionary[name].mode;
}
}
+64
View File
@@ -0,0 +1,64 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour {
//public access members
public GameObject targetObject;
public Vector3 offset;
public float lerpSpeed = 2f;
Vector2 peek = new Vector2(0, 0);
//private members
Vector3 virtualLocation;
void Start() {
virtualLocation = transform.position;
}
void Update() {
//only continue if the target has been set
if (targetObject == null) {
return;
}
//cache the position we want to move to
Vector3 targetPosition = targetObject.transform.position + offset + new Vector3(peek.x, peek.y, 0f);
//If the distance is small, short circuit the lerp, so we don't have sudden pops in camera motion.
if ((targetPosition - virtualLocation).sqrMagnitude > 0.01f) {
//Interpolate to the target location.
virtualLocation = Vector3.Lerp(virtualLocation, targetPosition, lerpSpeed * Time.deltaTime);
//Snap to pixel coordinates
Vector3 snapped = virtualLocation;
snapped.x = Mathf.Round(snapped.x * 100) / 100;
snapped.y = Mathf.Round(snapped.y * 100) / 100;
snapped.z = transform.position.z; //BUGFIX
transform.position = snapped;
}
}
public Vector2 GetPeek() {
return peek;
}
public void SetPeek(Vector2 newPeek, float delay = 0.5f) {
peek = new Vector2(0f, 0f);
StartCoroutine(SetPeekAfter(delay, newPeek)); //NOTE: a delay to peeking, for smooth gameplay
}
IEnumerator SetPeekAfter(float delay, Vector2 addition) {
yield return new WaitForSeconds(delay);
peek = addition;
}
public void ResetPeek() {
peek = new Vector2(0, 0);
}
}
@@ -0,0 +1,178 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Creatures {
public class CockatooController : MonoBehaviour, ICreature {
//public access members
public GameObject projectilePrefab;
//components
Rigidbody2D rigidBody;
//gameplay
const float moveForce = 10f;
const float maxSpeed = 2.5f;
int _horizontalMoveDirection;
public int HorizontalMoveDirection {
get {
return _horizontalMoveDirection;
}
set {
_horizontalMoveDirection = value;
if (_horizontalMoveDirection >= 0) {
transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), Mathf.Abs(transform.localScale.y));
} else {
transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), Mathf.Abs(transform.localScale.y));
}
}
}
public int VerticalMoveDirection { get; set; }
public int DamageValue { get; set; }
int _healthValue;
public int HealthValue {
get {
return _healthValue;
}
set {
_healthValue = value;
if (_healthValue <= 0) {
Destroy(gameObject);
}
}
}
//internals
float initialPositionY;
bool detectedPlayer;
float detectionDistance = 4f;
float lastDetection = float.NegativeInfinity;
float detectionDelay = 2f;
void Awake() {
rigidBody = GetComponent<Rigidbody2D>();
HorizontalMoveDirection = -1;
VerticalMoveDirection = 0;
HealthValue = 1;
DamageValue = 1;
initialPositionY = rigidBody.position.y;
rigidBody.position = new Vector2(rigidBody.position.x, rigidBody.position.y + 0.5f);
StartCoroutine(BigFlap(5f));
}
void FixedUpdate() {
HandleDetection();
HandleVerticalMoveDirection();
HandleMovement();
}
void OnCollisionEnter2D(Collision2D collision) {
Vector2 normal = collision.GetContact(0).normal;
if (collision.gameObject.tag == "Monster") {
//turn around
if (SameSign(collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection, HorizontalMoveDirection) || collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection == 0) {
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
//collision with the player (when the player is not bouncing)
if (collision.gameObject.tag == "Player" && normal != Vector2.down) {
//turn around
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
void HandleDetection() {
bool detectedPlayer = Physics2D.Linecast(transform.position, transform.position + new Vector3((transform.localScale.x > 0 ? 1 : -1) * detectionDistance, -detectionDistance, 0), 1 << LayerMask.NameToLayer("Player"));
if (detectedPlayer && Time.time - lastDetection > detectionDelay) {
lastDetection = Time.time;
GameObject go = Instantiate(projectilePrefab, transform.position, Quaternion.identity);
go.GetComponent<CockatooProjectileController>().HorizontalMoveDirection = HorizontalMoveDirection;
go.GetComponent<CockatooProjectileController>().VerticalMoveDirection = -1;
go.GetComponent<CockatooProjectileController>().DamageValue = DamageValue;
}
}
void HandleVerticalMoveDirection() {
if (rigidBody.position.y >= initialPositionY) {
VerticalMoveDirection = -1;
} else if (rigidBody.position.y < initialPositionY) {
VerticalMoveDirection = 1;
}
}
void HandleMovement() {
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
StartCoroutine(SetDirectionIfNotMovingAfter(-HorizontalMoveDirection, 0.1f));
}
//move the entity in this direction, if not at max speed
if (Mathf.Abs(rigidBody.velocity.x) < maxSpeed) {
rigidBody.AddForce(Vector2.right * HorizontalMoveDirection * moveForce);
}
//move the entity in the correct direction vertically
if (rigidBody.velocity.y * VerticalMoveDirection < maxSpeed) {
rigidBody.AddForce(Vector2.up * VerticalMoveDirection * moveForce);
}
//slow the entity down when it's travelling too fast
if (Mathf.Abs (rigidBody.velocity.x) > maxSpeed) {
rigidBody.velocity = new Vector2 (Mathf.Sign (rigidBody.velocity.x) * maxSpeed, rigidBody.velocity.y);
}
if (Mathf.Abs (rigidBody.velocity.y) > maxSpeed) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x, Mathf.Sign (rigidBody.velocity.y) * maxSpeed);
}
}
//utilities
IEnumerator SetDirectionIfNotMovingAfter(int direction, float delay) {
yield return new WaitForSeconds(delay);
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
HorizontalMoveDirection = direction;
}
}
//BUGFIX
IEnumerator BigFlap(float delay) {
while (true) {
yield return new WaitForSeconds(delay);
rigidBody.AddForce(Vector2.up * VerticalMoveDirection * moveForce * 5f);
}
}
void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + new Vector3((transform.localScale.x > 0 ? 1 : -1) * detectionDistance, -detectionDistance, 0));
}
bool SameSign(float num1, float num2) {
if (num1 > 0 && num2 > 0) {
return true;
}
if (num1 < 0 && num2 < 0) {
return true;
}
//if either is zero, return false
return false;
}
}
}
@@ -0,0 +1,60 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Creatures {
public class CockatooProjectileController : MonoBehaviour {
//public access members
public int HorizontalMoveDirection { get; set; }
public int VerticalMoveDirection { get; set; }
public int DamageValue { get; set; }
//internal members
Rigidbody2D rigidBody;
const float moveForce = 10f;
const float maxSpeed = 5f;
DamagerController damagerController;
void Start() {
rigidBody = GetComponent<Rigidbody2D>();
damagerController = GetComponent<DamagerController>();
damagerController.PushOnTriggerEnter((Collider2D collider) => {
if (collider.gameObject.tag == "Player") {
collider.gameObject.GetComponent<PlayerController>().HealthValue -= DamageValue;
}
Destroy(gameObject);
});
}
void FixedUpdate() {
HandleMovement();
//handle grapphics
transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x) * HorizontalMoveDirection, transform.localScale.y);
}
void HandleMovement() {
//move the entity in this direction, if not at max speed
if (Mathf.Abs(rigidBody.velocity.x) < maxSpeed) {
rigidBody.AddForce(Vector2.right * HorizontalMoveDirection * moveForce);
}
if (Mathf.Abs(rigidBody.velocity.y) < maxSpeed) {
rigidBody.AddForce(Vector2.up * VerticalMoveDirection * moveForce);
}
//slow the entity down when it's travelling too fast
if (Mathf.Abs (rigidBody.velocity.x) > maxSpeed) {
rigidBody.velocity = new Vector2 (Mathf.Sign (rigidBody.velocity.x) * maxSpeed, rigidBody.velocity.y);
}
if (Mathf.Abs (rigidBody.velocity.y) > maxSpeed) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x, Mathf.Sign (rigidBody.velocity.y) * maxSpeed);
}
}
}
}
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Creatures {
public interface ICreature {
//flags used to control other monsters
int HorizontalMoveDirection { get; set; }
int VerticalMoveDirection { get; set; }
//used by the combad system
int DamageValue { get; set; }
int HealthValue { get; set; }
}
}
@@ -0,0 +1,127 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Creatures {
public class KuriboController : MonoBehaviour, ICreature {
//components
Rigidbody2D rigidBody;
//gameplay
const float moveForce = 10f;
const float maxSpeed = 2.5f;
public int HorizontalMoveDirection { get; set; }
public int VerticalMoveDirection { get; set; }
public int DamageValue { get; set; }
int _healthValue;
public int HealthValue {
get {
return _healthValue;
}
set {
_healthValue = value;
if (_healthValue <= 0) {
Destroy(gameObject);
}
}
}
//internals
DamagerController damagerController;
void Awake() {
rigidBody = GetComponent<Rigidbody2D>();
HorizontalMoveDirection = -1;
VerticalMoveDirection = 0;
HealthValue = 1;
DamageValue = 1;
}
void Start() {
damagerController = GetComponentInChildren<DamagerController>();
damagerController.PushOnTriggerEnter((Collider2D collider) => {
if (collider.gameObject.tag == "Player") {
//deal damage to the player
collider.gameObject.GetComponent<PlayerController>().HealthValue -= DamageValue;
//NOTE: not every damager will deal damage
}
});
}
void FixedUpdate() {
HandleMovement();
}
void OnCollisionEnter2D(Collision2D collision) {
//handle bouncing on a monster
Vector2 normal = collision.GetContact(0).normal;
if (collision.gameObject.tag == "Monster") {
if (normal == Vector2.up) {
//bounce
rigidBody.AddForce(new Vector2(0f, 480f));
} else {
//turn around
if (SameSign(collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection, HorizontalMoveDirection) || collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection == 0) {
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
}
//collision with the player (when the player is not bouncing)
if (collision.gameObject.tag == "Player" && normal != Vector2.down) {
//turn around
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
void HandleMovement() {
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
StartCoroutine(SetDirectionIfNotMovingAfter(-HorizontalMoveDirection, 0.1f));
}
//move the entity in this direction, if not at max speed
if (Mathf.Abs(rigidBody.velocity.x) < maxSpeed) {
rigidBody.AddForce(Vector2.right * HorizontalMoveDirection * moveForce);
}
//slow the entity down when it's travelling too fast
if (Mathf.Abs (rigidBody.velocity.x) > maxSpeed) {
rigidBody.velocity = new Vector2 (Mathf.Sign (rigidBody.velocity.x) * maxSpeed, rigidBody.velocity.y);
}
if (Mathf.Abs (rigidBody.velocity.y) > maxSpeed) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x, Mathf.Sign (rigidBody.velocity.y) * maxSpeed);
}
}
//utilities
IEnumerator SetDirectionIfNotMovingAfter(int direction, float delay) {
yield return new WaitForSeconds(delay);
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
HorizontalMoveDirection = direction;
}
}
bool SameSign(float num1, float num2) {
if (num1 > 0 && num2 > 0) {
return true;
}
if (num1 < 0 && num2 < 0) {
return true;
}
//if either is zero, return false
return false;
}
}
}
@@ -0,0 +1,173 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Creatures {
public class WolfController : MonoBehaviour, ICreature {
//components
SpriteRenderer spriteRenderer;
Rigidbody2D rigidBody;
//gameplay
const float moveForce = 10f;
const float walkSpeed = 2.5f;
const float runSpeed = 5f;
float maxSpeed;
int _horizontalMoveDirection;
public int HorizontalMoveDirection {
get {
return _horizontalMoveDirection;
}
set {
_horizontalMoveDirection = value;
if (_horizontalMoveDirection >= 0) {
transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), Mathf.Abs(transform.localScale.y));
} else {
transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), Mathf.Abs(transform.localScale.y));
}
}
}
public int VerticalMoveDirection { get; set; }
public int DamageValue { get; set; }
int _healthValue;
public int HealthValue {
get {
return _healthValue;
}
set { //TODO: flash red
_healthValue = value;
if (_healthValue <= 0) {
Destroy(gameObject);
}
}
}
//internals
DamagerController damagerController;
bool detectedPlayer = false;
float detectionDistance = 10f;
void Awake() {
spriteRenderer = GetComponent<SpriteRenderer>();
rigidBody = GetComponent<Rigidbody2D>();
HorizontalMoveDirection = 1;
VerticalMoveDirection = 0;
HealthValue = 1;
DamageValue = 1;
}
void Start() {
damagerController = GetComponentInChildren<DamagerController>();
damagerController.PushOnTriggerEnter((Collider2D collider) => {
if (collider.gameObject.tag == "Player") {
//deal damage to the player
collider.gameObject.GetComponent<PlayerController>().HealthValue -= DamageValue;
//flip direction after a bite
HorizontalMoveDirection = -HorizontalMoveDirection;
//NOTE: not every damager will deal damage
}
});
damagerController.gameObject.SetActive(false);
}
void FixedUpdate() {
HandleDetection();
HandleMovement();
}
void OnCollisionEnter2D(Collision2D collision) {
//handle bouncing on a monster
Vector2 normal = collision.GetContact(0).normal;
if (collision.gameObject.tag == "Monster") {
if (normal == Vector2.up) {
//bounce
rigidBody.AddForce(new Vector2(0f, 480f));
} else {
//turn around
if (SameSign(collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection, HorizontalMoveDirection) || collision.gameObject.GetComponent<ICreature>().HorizontalMoveDirection == 0) {
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
}
//collision with the player (when the player is not bouncing)
if (collision.gameObject.tag == "Player" && normal != Vector2.down) {
//turn around
rigidBody.velocity = new Vector2(0f, rigidBody.velocity.y);
HorizontalMoveDirection = -HorizontalMoveDirection;
}
}
void HandleDetection() {
detectedPlayer = Physics2D.Linecast(transform.position, transform.position + new Vector3(transform.localScale.x * detectionDistance, 0, 0), 1 << LayerMask.NameToLayer("Player"));
if (detectedPlayer) {
maxSpeed = runSpeed;
spriteRenderer.color = Color.red;
damagerController.gameObject.SetActive(true);
} else { //no see the play
maxSpeed = walkSpeed;
spriteRenderer.color = Color.white;
damagerController.gameObject.SetActive(false);
}
}
void HandleMovement() {
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
StartCoroutine(SetDirectionIfNotMovingAfter(-HorizontalMoveDirection, 0.1f));
}
//move the entity in this direction, if not at max speed
if (Mathf.Abs(rigidBody.velocity.x) < maxSpeed) {
rigidBody.AddForce(Vector2.right * HorizontalMoveDirection * moveForce);
}
//slow the entity down when it's travelling too fast
if (Mathf.Abs (rigidBody.velocity.x) > maxSpeed) {
rigidBody.velocity = new Vector2 (Mathf.Sign (rigidBody.velocity.x) * maxSpeed, rigidBody.velocity.y);
}
if (Mathf.Abs (rigidBody.velocity.y) > maxSpeed) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x, Mathf.Sign (rigidBody.velocity.y) * maxSpeed);
}
}
//utilities
IEnumerator SetDirectionIfNotMovingAfter(int direction, float delay) {
yield return new WaitForSeconds(delay);
//turn around if stopped
if (Mathf.Abs(rigidBody.velocity.x) < 0.1f) {
HorizontalMoveDirection = direction;
}
}
void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + new Vector3(transform.localScale.x * detectionDistance, 0, 0));
}
bool SameSign(float num1, float num2) {
if (num1 > 0 && num2 > 0) {
return true;
}
if (num1 < 0 && num2 < 0) {
return true;
}
//if either is zero, return false
return false;
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamagerController : MonoBehaviour {
public delegate void CallbackHandler(Collider2D collider);
List<CallbackHandler> onTriggerEnter = new List<CallbackHandler>();
List<CallbackHandler> onTriggerStay= new List<CallbackHandler>();
List<CallbackHandler> onTriggerExit = new List<CallbackHandler>();
//public access members
public void PushOnTriggerEnter(CallbackHandler callback) {
onTriggerEnter.Add(callback);
}
public void PushOnTriggerStay(CallbackHandler callback) {
onTriggerStay.Add(callback);
}
public void PushOnTriggerExit(CallbackHandler callback) {
onTriggerExit.Add(callback);
}
public void PurgeLists() {
onTriggerEnter.Clear();
onTriggerStay.Clear();
onTriggerExit.Clear();
}
//monobehaviour members
void OnTriggerEnter2D(Collider2D collider) {
foreach(CallbackHandler callback in onTriggerEnter) {
callback(collider);
}
}
void OnTriggerStay2D(Collider2D collider) {
foreach(CallbackHandler callback in onTriggerStay) {
callback(collider);
}
}
void OnTriggerExit2D(Collider2D collider) {
foreach(CallbackHandler callback in onTriggerExit) {
callback(collider);
}
}
}
+715
View File
@@ -0,0 +1,715 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SpriterDotNetUnity;
public class PlayerController : MonoBehaviour {
//internal components
GameObject spriteObject;
UnityAnimator animator;
Rigidbody2D rigidBody;
BoxCollider2D currentBoxCollider;
//constants
const float deadZone = 0.25f;
//gameplay
[Header("Movement Settings")]
public float moveForce = 10f;
public float jumpForce = 400;
public float maxSpeed = 3f;
public float fallSpeed = 8f;
public float dashMultiplier = 2f;
public float straightJumpVerticalMultiplier = 1.1f;
public float straightJumpHorizontalMultiplier = 0.5f;
// public float rollingJumpVerticalMultiplier = 1.0f;
// public float rollingJumpHorizontalMultiplier = 1.0f;
public float jumpMaxSpeedMultiplier = 1.1f;
public float wallJumpMaxSpeedMultiplier = 0.5f;
public float wallHuggedMultiplier = 0.5f;
//basic movement
float horizontalInput = 0f;
float verticalInput = 0f;
bool jumping = false;
bool grounded = false;
bool wallHugged = false;
bool wallJumping = false;
const float groundedProjection = 0.08f;
//dashing movement
float dashValue = 0f;
float dashTime = float.NegativeInfinity;
bool dashLatch = true;
//movement modifiers
float horizontalModifier = 1f;
float verticalModifier = 1f;
float dashModifier = 1f;
float maxSpeedModifier = 1f;
//combat
DamagerController[] damagerControllers;
HUDCanvas hud;
bool invulnerable = false;
public int DamageValue { get; set; }
public int HealthValue {
set {
//prevent damage while invulnerable
if (value < hud.FlameLevel && invulnerable) {
return;
}
//check death
if (value < 0) {
Debug.Log("YOU DIED");
}
//trigger hitstun if damage taken
if (value < hud.FlameLevel) {
StartCoroutine(TriggerHitStun(0.8f));
}
//set the graphics
hud.FlameLevel = value;
}
get {
return hud.FlameLevel;
}
}
//graphical modifiers
float prevTimeScale;
float prevLocalScaleX;
//gameplay
float friction = 0f;
void Awake() {
rigidBody = GetComponent<Rigidbody2D>();
currentBoxCollider = GetComponent<BoxCollider2D>();
prevTimeScale = Time.timeScale; //BUGFIX: waking when unpaused
prevLocalScaleX = transform.localScale.x;
}
void Start() {
//BUGFIX: Set the sprite sorting order
GetComponentsInChildren<SpriterDotNetBehaviour>()[0].SortingLayer = "Player";
//get the HUD
hud = Object.FindObjectOfType<HUDCanvas>();
DamageValue = 1;
HealthValue = 4;
//get and disable the damagers
damagerControllers = GetComponentsInChildren<DamagerController>();
foreach(DamagerController dmgr in damagerControllers) {
dmgr.gameObject.SetActive(false);
dmgr.PushOnTriggerEnter((Collider2D collider) => {
if (collider.gameObject.tag == "Monster" && collider.gameObject.GetComponent<Creatures.ICreature>().HealthValue > 0) {
collider.gameObject.GetComponent<Creatures.ICreature>().HealthValue -= DamageValue;
hud.SparkLevel++;
//bounce on attack
Vector2 normal = collider.bounds.ClosestPoint(transform.position) - transform.position;
normal.Normalize();
if (normal.y < 0f) {
jumping = true;
}
}
});
}
}
void Update() {
if (Time.timeScale > 0f && Time.timeScale == prevTimeScale && HandleAnimation()) {
HandleInput();
}
prevTimeScale = Time.timeScale;
//Debug.LogFormat("{0} {1}", animator.CurrentAnimation.Name, currentBoxCollider.size.y);
//handle gameplay
if (friction >= 1f) {
hud.SparkLevel++;
friction -= 1f;
}
if (animator.CurrentAnimation.Name != "Run" && animator.CurrentAnimation.Name != "Wall Slide") {
friction = 0f;
}
}
void FixedUpdate() {
HandleMovement();
}
void OnCollisionEnter2D(Collision2D collision) {
//handle bouncing on a monster
Vector2 normal = collision.GetContact(0).normal;
if (collision.gameObject.tag == "Monster") {
if (normal == Vector2.up) {
//bounce
jumping = true;
}
}
}
void HandleInput() {
//determine if on the ground (using coyote time)
bool trueGrounded;
trueGrounded = Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x), -groundedProjection, 0), 1 << LayerMask.NameToLayer("Ground"));
trueGrounded |= Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2.1f), -groundedProjection, 0), 1 << LayerMask.NameToLayer("Ground"));
trueGrounded |= Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x - currentBoxCollider.size.x / 2.1f), -groundedProjection, 0), 1 << LayerMask.NameToLayer("Ground"));
if (trueGrounded) {
grounded = true;
} else {
StartCoroutine(SetGroundedWithDelay(false, 0.1f)); //coyote physics: 100ms
}
//determine wall hugging
wallHugged = Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.05f, 0), 1 << LayerMask.NameToLayer("Ground"));
wallHugged &= Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.5f, 0), 1 << LayerMask.NameToLayer("Ground"));
wallHugged &= Physics2D.Linecast(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.95f, 0), 1 << LayerMask.NameToLayer("Ground"));
//reset multipliers under regular conditions
if (trueGrounded && !jumping) {
horizontalModifier = 1f;
verticalModifier = 1f;
maxSpeedModifier = 1f;
}
//get inputs
verticalInput = GamePad.GetAxis(CAxis.LY);
horizontalInput = GamePad.GetAxis(CAxis.LX);
//determine vertical input
if (Mathf.Abs(verticalInput) < deadZone) { //no input
verticalInput = 0f;
//handle stop crouching
if (animator.CurrentAnimation.Name == "Crouch") {
animator.Play("Crouch to Idle");
}
} else { //yes input
if (verticalInput < 0) { //looking up
if (grounded && animator.CurrentAnimation.Name == "Idle" && Mathf.Abs(horizontalInput) < deadZone) {
animator.Play("Idle to Lookup");
}
} else { //crouching down
//from rolling jump to crouch
if (grounded && animator.CurrentAnimation.Name == "Rolling Jump") {
animator.Play("Crouch");
}
else if (grounded && Mathf.Abs(rigidBody.velocity.y) > 0.0001f) { //explicitly don't check for animation here; "Straight Jump Landing" passes through here for some reason
animator.Play("Rolling Jump");
}
//if not already crouching
else if (grounded && animator.CurrentAnimation.Name != "Crouch" && animator.CurrentAnimation.Name != "Idle to Crouch") {
animator.Play("Idle to Crouch");
}
}
}
//determine if walking
if (Mathf.Abs(horizontalInput) < deadZone) { //no input
//reset multipliers under regular conditions
if (grounded && !jumping) {
dashModifier = 1f;
if (animator.CurrentAnimation.Name == "Run") {
animator.Play("Walk");
}
}
if (dashLatch) {
//capture the time of last release for dashing
dashTime = Time.time;
dashLatch = false;
}
//stop walking
horizontalInput = 0f;
if (animator.CurrentAnimation.Name == "Walk") {
//animator.Play("Walk to Idle"); //TODO: enable this
animator.Play("Idle"); //TODO: remove this (why?)
}
} else { //yes input
if (animator.CurrentAnimation.Name == "Crouch") {
//TODO: slides/rolls
horizontalInput = horizontalInput > 0 ? 0.0001f : -0.0001f; //TMP
} else if (animator.CurrentAnimation.Name == "Lookup") {
animator.Play("Lookup to Idle");
} else if (wallHugged && !grounded && (animator.CurrentAnimation.Name != "Brace on Wall" && animator.CurrentAnimation.Name != "Wall Slide" && animator.CurrentAnimation.Name != "Wall Kick")) {
animator.Play("Brace on Wall");
} else {
//check if dashing
if (Time.time - dashTime < 0.2f && SameSign(horizontalInput, dashValue) && grounded && !dashLatch) {
dashModifier = dashMultiplier;
horizontalInput = maxSpeed * dashModifier * (horizontalInput > 0 ? 1 : -1);
animator.Play("Run");
StartCoroutine(BuildingFriction("Run", 0.05f));
}
//capture the value for dashing
dashValue = horizontalInput;
//BUGFIX
dashLatch = true;
}
//BUGFIX: landing into a run
if (animator.CurrentAnimation.Name == "Idle" && dashModifier != 1f) {
animator.Play("Run");
StartCoroutine(BuildingFriction("Run", 0.05f));
}
//start walking
if (animator.CurrentAnimation.Name == "Idle") {
animator.Play("Idle to Walk");
}
//flip direction
if (Time.timeScale > 0f) {
prevLocalScaleX = transform.localScale.x;
transform.localScale = new Vector3(horizontalInput > 0 ? 1 : -1, 1, 1);
//play turning animations
if (prevLocalScaleX != transform.localScale.x) {
if (animator.CurrentAnimation.Name == "Idle" || animator.CurrentAnimation.Name == "Idle to Walk" || animator.CurrentAnimation.Name == "Walk" || animator.CurrentAnimation.Name == "Lookup to Idle") {
animator.Play("Ground Turn");
}
if (animator.CurrentAnimation.Name == "Straight Jump Rising" || animator.CurrentAnimation.Name == "Straight Jump Crest" || animator.CurrentAnimation.Name == "Straight Jump Falling") {
animator.Play("Straight Jump Turn");
}
if (animator.CurrentAnimation.Name == "Rolling Jump") {
animator.Play("Rolling Jump Turn");
}
}
}
}
//determine if jumping
if (GamePad.GetState().Pressed(CButton.A) && grounded) {
jumping = true;
maxSpeedModifier = jumpMaxSpeedMultiplier;
if (animator.CurrentAnimation.Name == "Crouch") {
animator.Play("Rolling Jump");
}
else if (Mathf.Abs(horizontalInput) < deadZone) {
animator.Play("Begin Straight Jump");
horizontalModifier = straightJumpHorizontalMultiplier;
verticalModifier = straightJumpVerticalMultiplier;
} else {
animator.Play("Begin Rolling Jump");
//horizontalModifier = rollingJumpHorizontalMultiplier;
//verticalModifier = rollingJumpVerticalMultiplier;
}
}
if (GamePad.GetState().Pressed(CButton.A) && !grounded && wallHugged && (animator.CurrentAnimation.Name == "Brace on Wall" || animator.CurrentAnimation.Name == "Wall Slide")) {
wallJumping = true;
maxSpeedModifier = jumpMaxSpeedMultiplier;
animator.Play("Wall Kick");
}
//determine if attacking on the ground
if (GamePad.GetState().Pressed(CButton.B) && grounded) {
if (verticalInput < -deadZone) { //yes up input
if (animator.CurrentAnimation.Name == "Run") {
animator.Play("Grounded Upward Slash"); //TODO: replace with "Running Upward Slash"
} else {
animator.Play("Grounded Upward Slash");
}
StartCoroutine(EnableDamagerForPeriod(0, 0.3f));
} else { //no vertical input
if (animator.CurrentAnimation.Name == "Run") {
animator.Play("Grounded Forward Slash"); //TODO: replace with "Running Forward Slash"
} else {
animator.Play("Grounded Forward Slash");
}
StartCoroutine(EnableDamagerForPeriod(1, 0.3f));
}
}
//determine if attacking in the air
if (GamePad.GetState().Pressed(CButton.B) && !grounded) {
if (verticalInput < -deadZone) { //yes up input
animator.Play("Airborn Upward Slash");
StartCoroutine(EnableDamagerForPeriod(0, 0.3f));
} else if (verticalInput > deadZone) { //yes down input
animator.Play("Airborn Downward Slash");
StartCoroutine(EnableDamagerForPeriod(2, 0.3f));
} else { //no vertical input
animator.Play("Airborn Forward Slash");
StartCoroutine(EnableDamagerForPeriod(1, 0.3f));
}
}
//determine if releasing the attack button
if (GamePad.GetState().Released(CButton.B)) {
foreach (DamagerController dmgr in damagerControllers) {
dmgr.gameObject.SetActive(false);
}
}
//BUGFIX: prevent crouch-gliding and slash-gliding
if (grounded && (animator.CurrentAnimation.Name == "Crouch")) {
horizontalInput = 0f;
dashModifier = 1f;
}
//BUGFIX: falling animations after walking/running off a cliff
if (rigidBody.velocity.y < -0.0001f && (animator.CurrentAnimation.Name == "Walk" || animator.CurrentAnimation.Name == "Run")) {
animator.Play("Straight Jump Falling");
}
}
void HandleMovement() {
//stop the player if input in that direction has been removed
if (horizontalInput * rigidBody.velocity.x <= 0 && grounded) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x * 0.85f, rigidBody.velocity.y);
}
//move in the inputted direction, if not at max speed
if (horizontalInput * rigidBody.velocity.x < maxSpeed * dashModifier * maxSpeedModifier) {
rigidBody.AddForce (Vector2.right * horizontalInput * moveForce * horizontalModifier);
}
//slow the player down when it's travelling too fast
if (Mathf.Abs (rigidBody.velocity.x) > maxSpeed * dashModifier * maxSpeedModifier) {
rigidBody.velocity = new Vector2 (Mathf.Sign (rigidBody.velocity.x) * maxSpeed * dashModifier * maxSpeedModifier, rigidBody.velocity.y);
}
if (rigidBody.velocity.y < -fallSpeed * (wallHugged ? wallHuggedMultiplier : 1f)) {
rigidBody.velocity = new Vector2 (rigidBody.velocity.x, Mathf.Sign (rigidBody.velocity.y) * fallSpeed * (wallHugged ? wallHuggedMultiplier : 1f));
}
//jump up
if (jumping) {
rigidBody.velocity = new Vector2(rigidBody.velocity.x, 0f); //max v-jump speed
rigidBody.AddForce (new Vector2 (0f, jumpForce * verticalModifier));
jumping = false;
}
if (wallJumping) {
rigidBody.velocity = new Vector2(0f, 0f); //wall-jump from zero
rigidBody.AddForce (new Vector2 (-transform.localScale.x * maxSpeed * jumpForce, jumpForce * verticalModifier));
maxSpeedModifier = wallJumpMaxSpeedMultiplier;
wallJumping = false;
}
}
bool HandleAnimation() {
if (spriteObject == null) {
foreach (Transform child in transform) {
if (child.name == "Ember") {
spriteObject = child.gameObject;
break;
}
}
}
if (animator == null && spriteObject != null) {
animator = spriteObject.GetComponent<SpriterDotNetBehaviour>().Animator;
animator.AnimationFinished += HandleAnimationTransitions;
}
//determine statue state
if (!PauseManager.Instance.Paused && animator.CurrentAnimation.Name == "Statue" &&
(
//NOTE: carbon input really needs an "any key"
//any face button
GamePad.GetState().Pressed(CButton.A) || GamePad.GetState().Pressed(CButton.B) || GamePad.GetState().Pressed(CButton.X) || GamePad.GetState().Pressed(CButton.Y) || //only pressed this loop
//any axis
Mathf.Abs(GamePad.GetAxis(CAxis.LX)) >= deadZone || Mathf.Abs(GamePad.GetAxis(CAxis.LY)) >= deadZone || Mathf.Abs(GamePad.GetAxis(CAxis.RX)) >= deadZone || Mathf.Abs(GamePad.GetAxis(CAxis.RY)) >= deadZone
)
) {
animator.Play("Statue to Idle");
return false;
}
if (animator.CurrentAnimation.Name == "Statue to Idle") {
return false;
}
//switch from regular animations to transition animations
if (animator.CurrentAnimation.Name == "Straight Jump Rising" && rigidBody.velocity.y <= 0) {
animator.Play("Straight Jump Crest");
}
if (animator.CurrentAnimation.Name == "Straight Jump Falling" && rigidBody.velocity.y >= 0) {
animator.Play("Straight Jump Landing");
}
if (animator.CurrentAnimation.Name == "Rolling Jump" && grounded) {
animator.Play("Straight Jump Landing"); //deliberately reuse this
}
//start looking up
if (animator.CurrentAnimation.Name == "Idle" && verticalInput < 0 && Mathf.Abs(rigidBody.velocity.x) < 0.0001f) {
animator.Play("Idle to Lookup");
}
//stop looking up
if (animator.CurrentAnimation.Name == "Lookup" && verticalInput >= 0) {
animator.Play("Lookup to Idle");
}
//BUGFIX: bouncing off of a wall
if (!grounded && !wallHugged && animator.CurrentAnimation.Name == "Wall Slide") {
animator.Play("Rolling Jump");
}
//if ever simply falling (or attacking)
if (!grounded && !wallHugged && rigidBody.velocity.y < 0 && animator.CurrentAnimation.Name != "Straight Jump Crest" && animator.CurrentAnimation.Name != "Straight Jump Turn" && animator.CurrentAnimation.Name != "Rolling Jump Turn" && animator.CurrentAnimation.Name != "Rolling Jump" && animator.CurrentAnimation.Name != "Airborn Upward Slash" && animator.CurrentAnimation.Name != "Airborn Downward Slash" && animator.CurrentAnimation.Name != "Airborn Forward Slash") {
animator.Play("Straight Jump Falling");
}
//hit a wall
if (animator.CurrentAnimation.Name != "Brace on Wall" && animator.CurrentAnimation.Name != "Wall Slide" && animator.CurrentAnimation.Name != "Wall Kick" && wallHugged && !grounded) {
animator.Play("Brace on Wall");
}
//reach the bottom of a wall-slide (or hit the ground when turning)
if ((animator.CurrentAnimation.Name == "Brace on Wall" || animator.CurrentAnimation.Name == "Wall Slide" || animator.CurrentAnimation.Name == "Straight Jump Turn" || animator.CurrentAnimation.Name == "Rolling Jump Turn") && grounded) {
animator.Play("Straight Jump Landing");
}
//if bouncing
if (animator.CurrentAnimation.Name == "Straight Jump Landing" && !grounded && rigidBody.velocity.y > 0f) {
animator.Play("Begin Rolling Jump");
}
//handle bounding box
if (
animator.CurrentAnimation.Name == "Crouch" ||
animator.CurrentAnimation.Name == "Idle to Crouch" ||
animator.CurrentAnimation.Name == "Rolling Jump" ||
animator.CurrentAnimation.Name == "Begin Rolling Jump" ||
(rigidBody.velocity.y > 0 && animator.CurrentAnimation.Name == "Straight Jump Landing") ||
animator.CurrentAnimation.Name == "Brace on Wall" ||
animator.CurrentAnimation.Name == "Wall Slide" ||
animator.CurrentAnimation.Name == "Wall Kick"
) {
//half box
if (currentBoxCollider != GetComponents<BoxCollider2D>()[1]) {
currentBoxCollider.enabled = false;
currentBoxCollider = GetComponents<BoxCollider2D>()[1];
currentBoxCollider.enabled = true;
}
} else {
//full box
if (currentBoxCollider != GetComponents<BoxCollider2D>()[0]) {
currentBoxCollider.enabled = false;
currentBoxCollider = GetComponents<BoxCollider2D>()[0];
currentBoxCollider.enabled = true;
}
}
//peek with the camera
CameraController camController = Object.FindObjectOfType<CameraController>();
if (camController.GetPeek() == Vector2.zero) {
if (animator.CurrentAnimation.Name == "Lookup") {
camController.SetPeek(new Vector2(0f, 4f));
}
if (animator.CurrentAnimation.Name == "Crouch") {
camController.SetPeek(new Vector2(0f, -4f));
}
}
if (camController.GetPeek() != Vector2.zero) {
if (animator.CurrentAnimation.Name != "Lookup" && animator.CurrentAnimation.Name != "Crouch") {
camController.ResetPeek();
}
}
return true;
}
//internal callbacks
void HandleAnimationTransitions(string name) {
//NOTE: This handles the end of transitional animations
switch(name) {
case "Statue to Idle":
animator.Play("Idle");
break;
case "Idle to Walk":
animator.Play("Walk");
break;
case "Walk to Idle":
animator.Play("Idle");
break;
case "Begin Straight Jump":
animator.Play("Straight Jump Rising");
break;
case "Straight Jump Crest":
animator.Play("Straight Jump Falling");
break;
case "Straight Jump Landing":
animator.Play("Idle");
break;
case "Begin Rolling Jump":
animator.Play("Rolling Jump");
break;
case "Idle to Crouch":
animator.Play("Crouch");
break;
case "Crouch to Idle":
animator.Play("Idle");
break;
case "Idle to Lookup":
animator.Play("Lookup");
break;
case "Lookup to Idle":
animator.Play("Idle");
break;
case "Brace on Wall":
animator.Play("Wall Slide");
StartCoroutine(BuildingFriction("Wall Slide", 0.1f));
break;
case "Wall Kick":
animator.Play("Rolling Jump");
break;
case "Grounded Forward Slash":
animator.Play("Idle");
break;
case "Grounded Upward Slash":
if (verticalInput < -deadZone && Mathf.Abs(rigidBody.velocity.x) < 0.0001f) {
animator.Play("Lookup");
} else {
if (dashModifier != 1f) {
animator.Play("Run");
StartCoroutine(BuildingFriction("Run", 0.05f));
} else {
animator.Play("Idle");
}
}
break;
case "Airborn Upward Slash":
case "Airborn Downward Slash":
case "Airborn Forward Slash":
if (rigidBody.velocity.y > 0f) {
animator.Play("Straight Jump Rising");
} else {
animator.Play("Straight Jump Falling");
}
break;
case "Ground Turn":
animator.Play("Idle");
break;
case "Straight Jump Turn":
if (rigidBody.velocity.y > 0f) {
animator.Play("Straight Jump Rising");
} else {
animator.Play("Straight Jump Falling");
}
break;
case "Rolling Jump Turn":
animator.Play("Rolling Jump");
break;
}
}
//utilities
void OnDrawGizmos() {
if (currentBoxCollider != null) {
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x), -groundedProjection, 0));
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2.1f), -groundedProjection, 0));
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x - currentBoxCollider.size.x / 2.1f), -groundedProjection, 0));
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.05f, 0));
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.5f, 0));
Gizmos.DrawLine(transform.position + new Vector3(currentBoxCollider.offset.x, currentBoxCollider.offset.y, 0), transform.position + new Vector3(transform.localScale.x * (currentBoxCollider.offset.x + currentBoxCollider.size.x / 2 + groundedProjection), currentBoxCollider.size.y * 0.95f, 0));
}
}
bool SameSign(float num1, float num2) {
if (num1 > 0 && num2 > 0) {
return true;
}
if (num1 < 0 && num2 < 0) {
return true;
}
//if either is zero, return false
return false;
}
IEnumerator SetGroundedWithDelay(bool value, float delay) {
yield return new WaitForSeconds(delay);
grounded = value;
}
IEnumerator EnableDamagerForPeriod(int index, float delay) {
damagerControllers[index].gameObject.SetActive(true);
yield return new WaitForSeconds(delay);
damagerControllers[index].gameObject.SetActive(false);
}
IEnumerator BuildingFriction(string animation, float increment) {
while (animator.CurrentAnimation.Name == animation) {
friction += increment;
yield return new WaitForSeconds(0.1f);
}
}
IEnumerator TriggerHitStun(float delay) {
invulnerable = true;
StartCoroutine(TriggerHitStunGraphic(0.1f, delay - 0.1f));
yield return new WaitForSeconds(delay);
invulnerable = false;
}
IEnumerator TriggerHitStunGraphic(float redDelay, float opacityDelay) {
SpriteRenderer[] spriteRenderers = GetComponentsInChildren<SpriteRenderer>();
//flash red
foreach (var renderer in spriteRenderers) {
renderer.color = Color.red;
}
yield return new WaitForSeconds(redDelay);
foreach (var renderer in spriteRenderers) {
renderer.color = Color.white;
renderer.material.color = new Color(1f, 1f, 1f, 0.75f);
}
yield return new WaitForSeconds(opacityDelay);
foreach (var renderer in spriteRenderers) {
renderer.material.color = Color.white;
}
}
}
@@ -0,0 +1,36 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProximitySpawner : MonoBehaviour {
//public access members
public GameObject prefab;
public string targetTag;
public float minDistance = 4f;
public float maxDistance = 10f;
public bool destroyIfTooFar = false;
//private members
Transform targetTransform;
GameObject spawnedObject;
void Start() {
targetTransform = GameObject.FindWithTag(targetTag).transform;
}
void Update() {
//get the distance
float distance = Vector3.Distance(transform.position, targetTransform.position);
//destroy if the player is too far away
if (destroyIfTooFar && distance > maxDistance) {
Destroy(spawnedObject);
return; //skip out on the next check
}
//check the distance, check the spawned object
if (distance > minDistance && distance < maxDistance && spawnedObject == null) {
spawnedObject = Instantiate(prefab);
}
}
}
@@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimedSpawner : MonoBehaviour {
//inspector access elements
public GameObject prefab;
public float spawnDelay = 10f;
public int maxSpawns = 3;
//private members
float lastSpawnTime = float.NegativeInfinity;
List<GameObject> spawnList = new List<GameObject>();
//TODO: maybe all spawners should shut off (and monsters become inactive) when the player is too far away.
void Update() {
//prune the list
spawnList.RemoveAll(spawn => spawn == null);
//spawn if enough time has passed
if (Time.time - lastSpawnTime > spawnDelay && spawnList.Count < maxSpawns) {
lastSpawnTime = Time.time;
spawnList.Add(Instantiate(prefab, transform.position, Quaternion.identity, transform));
}
}
}
@@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Structures {
public class HazardController : MonoBehaviour {
//internals
int DamageValue { get; set; }
DamagerController damagerController;
void Awake() {
DamageValue = 1;
}
void Start() {
damagerController = GetComponent<DamagerController>();
damagerController.PushOnTriggerStay((Collider2D collider) => {
if (collider.gameObject.tag == "Player") {
//deal damage to the player
collider.gameObject.GetComponent<PlayerController>().HealthValue -= DamageValue;
//NOTE: not every damager will deal damage
}
});
}
}
}
@@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Structures {
public class Pedestal : MonoBehaviour {
public string name;
public SaveHandler SaveHandler { set; get; }
void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.tag == "Player") {
SaveFileManager.LoadedSaveSlot.currentLocation = name;
SaveHandler.saveMenuAvailable = true;
}
}
void OnCollisionExit2D(Collision2D collision) {
if (collision.gameObject.tag == "Player") {
SaveHandler.saveMenuAvailable = false;
}
}
}
}
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.IO;
using UnityEngine;
[Serializable()]
public class ConfigurationManager {
//singleton members
private static ConfigurationManager singletonObject = null;
public static ConfigurationManager Instance {
get {
if (singletonObject == null) {
string fname = Path.Combine(Application.persistentDataPath, "configuration.json");
new ConfigurationManager(fname); //NOTE: singleton object assigned elsewhere
}
return singletonObject;
}
}
//serializable fields (these can be null, so handle that elsewhere)
public string textSpeed;
public float volume;
//private internal members
static bool initialized = false; //BUGFIX: stack overflow
static string dataPath;
//methods
private ConfigurationManager(string fname) {
if (!initialized) {
initialized = true;
LoadData(fname);
dataPath = fname;
}
}
public void CleanUp() {
SaveData(Instance, dataPath);
}
void LoadData(string fname) {
if (!File.Exists(fname)) {
singletonObject = JsonUtility.FromJson<ConfigurationManager> ("{}");
return;
}
StreamReader streamReader = File.OpenText(fname);
string jsonString = streamReader.ReadToEnd();
streamReader.Close();
singletonObject = JsonUtility.FromJson<ConfigurationManager> (jsonString);
//reset statics
initialized = true;
}
void SaveData(ConfigurationManager configMgr, string fname) {
string jsonString = JsonUtility.ToJson(configMgr);
StreamWriter streamWriter = File.CreateText(fname);
streamWriter.Write(jsonString);
streamWriter.Close();
}
}
+65
View File
@@ -0,0 +1,65 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PauseManager {
//singleton members
private static PauseManager singletonObject = null;
public static PauseManager Instance {
get {
if (singletonObject != null) {
return singletonObject;
} else {
return singletonObject = new PauseManager();
}
}
set {
singletonObject = value;
}
}
//paused controller
bool paused = false;
public bool Paused {
get {
return paused;
}
set {
paused = value;
TriggerLists();
}
}
public delegate void CallbackHandler();
List<CallbackHandler> onPausedList = new List<CallbackHandler>();
List<CallbackHandler> onResumeList = new List<CallbackHandler>();
private PauseManager() {}
public void PushOnPaused(CallbackHandler callback) {
onPausedList.Add(callback);
}
public void PushOnResume(CallbackHandler callback) {
onResumeList.Add(callback);
}
public void PurgeLists() {
onPausedList.Clear();
onResumeList.Clear();
}
void TriggerLists() {
if (Paused) {
foreach(CallbackHandler callback in onPausedList) {
callback();
}
}
else {
foreach(CallbackHandler callback in onResumeList) {
callback();
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
using System;
using System.IO;
using UnityEngine;
public class SaveFileManager {
//public structures
[Serializable()]
public class SaveSlot {
//serializable fields (these can be null, so handle that elsewhere)
public string currentLocation; //name of the pedestal
public float secondsPlaying;
public string awardImage; //TODO: award image
//abilities
public bool flameBody;
public bool chargeSwipe;
public bool wallSlide;
public bool flameProjectile;
public bool flameWings;
}
//singleton members
private static SaveFileManager singletonObject = null;
public static SaveFileManager Instance {
get {
if (singletonObject == null) {
singletonObject = new SaveFileManager();
}
return singletonObject;
}
}
//private internal members
//
private SaveFileManager() {
//
}
//public members
public static string saveSlotFileName; //NOT part of the save structure; only one can be loaded into the game at a time
public static SaveSlot LoadedSaveSlot { get; set; }
//methods
public static SaveSlot LoadData(string fname) {
if (!File.Exists(fname)) {
return JsonUtility.FromJson<SaveSlot> ("{}");
}
StreamReader streamReader = File.OpenText(fname);
string jsonString = streamReader.ReadToEnd();
streamReader.Close();
return JsonUtility.FromJson<SaveSlot> (jsonString);
}
public static void SaveData(SaveSlot save, string fname) {
string jsonString = JsonUtility.ToJson(save);
StreamWriter streamWriter = File.CreateText(fname);
streamWriter.Write(jsonString);
streamWriter.Close();
}
public static SaveSlot CreateBlankSaveSlot() {
SaveSlot saveSlot = new SaveSlot();
saveSlot.currentLocation = "Start";
saveSlot.secondsPlaying = 0f;
saveSlot.awardImage = "";
saveSlot.flameBody = false;
saveSlot.chargeSwipe = false;
saveSlot.wallSlide = false;
saveSlot.flameProjectile = false;
saveSlot.flameWings = false;
return saveSlot;
}
}
+73
View File
@@ -0,0 +1,73 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SpriterDotNetUnity;
//DOCS: this is intended for Ember's foot step sounds
public class PlayerAudio : MonoBehaviour {
//public members
public AudioClip[] leftFootsteps;
public AudioClip[] rightFootsteps;
public AudioClip jumpSound;
public AudioClip landSound;
//internal members
GameObject spriteObject;
UnityAnimator animator;
AudioSource audioSource;
void Start() {
audioSource = GetComponent<AudioSource>();
}
void Update() {
//spriter object is handled as an animation
HandleAnimation();
}
// Update is called once per frame
void HandleAnimation() {
if (spriteObject == null) {
foreach (Transform child in transform) {
if (child.name == "Ember") {
spriteObject = child.gameObject;
break;
}
}
}
if (animator == null && spriteObject != null) {
animator = spriteObject.GetComponent<SpriterDotNetBehaviour>().Animator;
animator.EventTriggered += AudioTriggers;
}
}
void AudioTriggers(string name) {
switch(name) {
case "S: footstep left":
audioSource.PlayOneShot(leftFootsteps[PickASlot(leftFootsteps.Length)]);
break;
case "S: footstep right":
audioSource.PlayOneShot(rightFootsteps[PickASlot(rightFootsteps.Length)]);
break;
case "S: jump":
audioSource.PlayOneShot(jumpSound);
break;
case "S: land":
audioSource.PlayOneShot(landSound);
break;
}
}
int PickASlot(int length) {
for (int i = 0; i < length; i++) {
if (Random.Range(0, 2) == 0) return i;
}
return 0;
}
}
@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Startups {
public class Debugger : MonoBehaviour {
AudioController audioController;
void Start() {
audioController = Object.FindObjectOfType(typeof(AudioController)) as AudioController;
audioController.Load("rockstar", "Audio/Music/EngineTest");
audioController.Load("forest_ambience", "Audio/Music/Forest_Ambience");
audioController.Load("forest_background", "Audio/Music/Forest_Background");
audioController.Play("rockstar", AudioController.Mode.JUMP, 5f, 15);
// StartCoroutine(DebugLoopMusic(10f));
}
IEnumerator DebugLoopMusic(float duration) {
for(;;) {
audioController.PauseFadeOutAll(3f, new List<string> {"forest_background"});
audioController.UnpauseFadeIn("forest_background", 3f, AudioController.Mode.LOOP);
yield return new WaitForSeconds(duration);
audioController.PauseFadeOutAll(3f, new List<string> {"forest_ambience"});
audioController.UnpauseFadeIn("forest_ambience", 3f, AudioController.Mode.LOOP);
yield return new WaitForSeconds(duration);
}
}
}
}
@@ -0,0 +1,29 @@
using UnityEngine;
using System.Collections;
//DOCS: http://wiki.unity3d.com/index.php/FramesPerSecond
namespace Startups {
public class FPSDisplay : MonoBehaviour {
float deltaTime = 0.0f;
void Update() {
deltaTime += (Time.unscaledDeltaTime - deltaTime) * 0.1f;
}
void OnGUI() {
int w = Screen.width, h = Screen.height;
GUIStyle style = new GUIStyle();
Rect rect = new Rect(0, 0, w, h * 2 / 100);
style.alignment = TextAnchor.UpperLeft;
style.fontSize = h * 2 / 100;
style.normal.textColor = new Color (0.0f, 0.0f, 0.5f, 1.0f);
float msec = deltaTime * 1000.0f;
float fps = 1.0f / deltaTime;
string text = string.Format("{0:0.0} ms ({1:0.} fps)", msec, fps);
GUI.Label(rect, text, style);
}
}
}
@@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Startups {
public class PauseMenuHandler : MonoBehaviour {
public Canvas pauseMenuCanvas;
public Canvas optionsMenuCanvas;
public Canvas saveMenuCanvas;
PauseManager pauseManager;
void Start() {
pauseManager = PauseManager.Instance;
pauseManager.Paused = false;
pauseManager.PushOnPaused(() => {
pauseMenuCanvas.gameObject.SetActive(true);
Time.timeScale = 0f;
});
pauseManager.PushOnResume(() => {
pauseMenuCanvas.gameObject.SetActive(false);
optionsMenuCanvas.gameObject.SetActive(false);
saveMenuCanvas.gameObject.SetActive(false);
Time.timeScale = 1f;
});
}
void OnDestroy() {
pauseManager.PurgeLists();
}
void Update() {
if (GamePad.GetState().Pressed(CButton.Start)) {
pauseManager.Paused = !pauseManager.Paused;
}
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class SaveHandler : MonoBehaviour {
public GameObject playerObject;
public Dictionary<string, Structures.Pedestal> pedestalDictionary = new Dictionary<string, Structures.Pedestal>();
public bool saveMenuAvailable = false;
public float startTime = 0f;
void Start() {
//create the save file if it doesn't exist
if (SaveFileManager.LoadedSaveSlot == null) {
SaveFileManager.saveSlotFileName = Path.Combine(Application.persistentDataPath, DateTime.Now.ToString("yyyyMMddTHHmmss") + ".sav");
SaveFileManager.LoadedSaveSlot = SaveFileManager.CreateBlankSaveSlot();
// SaveFileManager.SaveData(SaveFileManager.LoadedSaveSlot, SaveFileManager.saveSlotFileName);
}
//initialize the game world with the given save data
startTime = Time.time;
//convert the array into a quick-search dictionary
Structures.Pedestal[] pedestals = GameObject.FindObjectsOfType<Structures.Pedestal>();
foreach(Structures.Pedestal pedestal in pedestals) {
pedestal.SaveHandler = this;
pedestalDictionary[pedestal.name] = pedestal;
}
//place the player on their saved pedestal
playerObject.transform.position = pedestalDictionary[SaveFileManager.LoadedSaveSlot.currentLocation].gameObject.transform.position;
}
}
@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Startups {
public class ConfigHandler : MonoBehaviour {
void OnDestroy() {
ConfigurationManager.Instance.CleanUp();
}
}
}
@@ -0,0 +1,158 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Must include Spriter Namespace
using SpriterDotNetUnity;
enum animationMode
{
lookUp,
lookDown,
move,
straightJump,
rollingJump,
}
public class EmberAnimationCycle : MonoBehaviour
{
/*
* Instead of putting the script on Spriter's automatically generated prefab, which will cause it to be unset every
* time Evan tweaks the damn animations, we put the control scripts on a seperate gameobject which becomes a parent
* of the Spriter prefab and take a reference to the Spriter object so we can manipulate it.
*/
public GameObject Ember;
// This is the actual thing we use to animate the character.
UnityAnimator anim;
void Start()
{
}
bool slash = false;
animationMode mode = animationMode.lookUp;
void Update()
{
if (Ember == null)
{
foreach (Transform child in transform)
{
if (child.name == "Ember") { Ember = child.gameObject; break;}
}
}
if (anim == null)
{
anim = Ember.GetComponent<SpriterDotNetBehaviour>().Animator;
//This event is fired whenever an animation ends.
anim.AnimationFinished += animationTransitions;
}
if (Input.anyKeyDown)
{
//Advance the animation.
if (anim.CurrentAnimation.Name == "Statue") anim.Play("Statue to Idle");
else if (anim.CurrentAnimation.Name == "Idle")
{
switch (mode)
{
case animationMode.move:
anim.Play("Idle to Walk");
mode = animationMode.straightJump;
break;
case animationMode.straightJump:
anim.Play("Begin Straight Jump");
mode = animationMode.rollingJump;
break;
case animationMode.rollingJump:
anim.Play("Begin Rolling Jump");
mode = animationMode.lookUp;
break;
case animationMode.lookUp:
if (!slash)
{
anim.Play("Grounded Forward Slash");
slash = true;
}
else
{
slash = false;
anim.Play("Idle to Lookup");
mode = animationMode.lookDown;
}
break;
case animationMode.lookDown:
anim.Play("Idle to Crouch");
mode = animationMode.move;
break;
}
}
else if (anim.CurrentAnimation.Name == "Walk") anim.Play("Run");
else if (anim.CurrentAnimation.Name == "Run") anim.Play("Idle");
else if (anim.CurrentAnimation.Name == "Straight Jump Rising")
{
if (!slash)
{
anim.Play("Airborn Upward Slash");
slash = true;
}
else
{
anim.Play("Straight Jump Crest");
slash = false;
}
}
else if (anim.CurrentAnimation.Name == "Straight Jump Falling"
|| anim.CurrentAnimation.Name == "Rolling Jump")
{
if (!slash)
{
if (anim.CurrentAnimation.Name == "Straight Jump Falling") anim.Play("Airborn Forward Slash");
else anim.Play("Airborn Downward Slash");
slash = true;
}
else
{
anim.Play("Straight Jump Landing");
slash = false;
}
}
else if (anim.CurrentAnimation.Name == "Crouch") anim.Play("Crouch to Idle");
else if (anim.CurrentAnimation.Name == "Lookup")
{
if (!slash)
{
anim.Play("Grounded Upward Slash");
slash = true;
}
else
{
anim.Play("Lookup to Idle");
slash = false;
}
}
}
}
void animationTransitions(string name)
{
//We check to see if it's one of our transition animations and if so advance to the animation
// we are transitioning to.
if (name == "Statue to Idle") anim.Play("Idle");
else if (name == "Idle to Walk") anim.Play("Walk");
else if (name == "Begin Straight Jump") anim.Play("Straight Jump Rising");
else if (name == "Straight Jump Crest") anim.Play("Straight Jump Falling");
else if (name == "Begin Rolling Jump") anim.Play("Rolling Jump");
else if (name == "Straight Jump Landing") anim.Play("Idle");
else if (name == "Idle to Crouch") anim.Play("Crouch");
else if (name == "Crouch to Idle") anim.Play("Idle");
else if (name == "Idle to Lookup") anim.Play("Lookup");
else if (name == "Lookup to Idle") anim.Play("Idle");
else if (name == "Grounded Forward Slash") anim.Play("Idle");
else if (name == "Grounded Upward Slash") anim.Play("Lookup");
else if (name == "Airborn Upward Slash") anim.Play("Straight Jump Rising");
else if (name == "Airborn Forward Slash") anim.Play("Straight Jump Falling");
else if (name == "Airborn Downward Slash") anim.Play("Rolling Jump");
}
}
@@ -0,0 +1,110 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
namespace DialogSystem {
public class DialogCanvas : MonoBehaviour {
//public access references
public Image dialogPanel;
public TextMeshProUGUI dialogText;
//internal variables
bool visible;
List<string> internalTextList;
//fine-tuning the interface
float speed = 0.25f;
float speedCharCount = 0;
void Start() {
SetVisible(false);
}
void Update() {
if (!PauseManager.Instance.Paused && visible) {
HandleInput();
}
}
void FixedUpdate() {
if (visible) {
ProcessSpeed();
ProcessText();
}
}
public void SetVisible(bool b) {
visible = b;
dialogPanel.gameObject.SetActive(visible);
dialogText.gameObject.SetActive(visible);
}
public bool GetVisible() {
return visible;
}
public void SetText(List<string> stringList, bool setVisible = true) {
internalTextList = stringList;
dialogText.text = "";
SetVisible(setVisible);
}
void HandleInput() {
if (GamePad.GetState().Pressed(CButton.A)) {
if (internalTextList[0].Length > speedCharCount) {
//skip the text scroll
speedCharCount = internalTextList[0].Length;
} else {
//skip to the next "page"
internalTextList.RemoveAt(0);
speedCharCount = 0;
//if there isn't another "page", go dormant
if (internalTextList.Count == 0) {
SetVisible(false);
}
}
}
}
void ProcessSpeed() {
switch(ConfigurationManager.Instance.textSpeed) {
case "Fast":
speed = 1f;
break;
case "Normal":
speed = 0.25f;
break;
case "Slow":
speed = 0.1f;
break;
}
}
void ProcessText() {
//if the list of text has run out
if (internalTextList.Count == 0) {
SetVisible(false);
return;
}
SetVisible(true);
//only show the first "speedCharCount" characters
string thisLine = internalTextList[0];
if (speedCharCount >= thisLine.Length) {
dialogText.text = thisLine;
return;
}
speedCharCount += speed;
thisLine = thisLine.Substring(0, (int)Mathf.Floor(speedCharCount));
dialogText.text = thisLine;
}
}
}
+76
View File
@@ -0,0 +1,76 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HUDCanvas : MonoBehaviour {
//public access members
public Sprite[] sparkSprites;
public Sprite[] flameSprites;
//public properties
int _sparkLevel = 0;
public int SparkLevel {
get {
return _sparkLevel;
}
set {
_sparkLevel = value;
if (_sparkLevel < 0) {
_sparkLevel = 0;
}
if (_sparkLevel > sparkSprites.Length * (flameSprites.Length - 1)) {
_sparkLevel = sparkSprites.Length * (flameSprites.Length - 1);
}
}
}
public int FlameLevel {
get {
return _sparkLevel / sparkSprites.Length;
}
set {
_sparkLevel = value * sparkSprites.Length + _sparkLevel % sparkSprites.Length;
while(_sparkLevel < 0) {
_sparkLevel += sparkSprites.Length;
}
while(_sparkLevel > sparkSprites.Length * (flameSprites.Length - 1)) {
_sparkLevel -= 1;
}
}
}
//internal members
Image[] childImages;
void Start() {
childImages = GetComponentsInChildren<Image>();
}
void Update() {
HandleGraphics();
DebugHandleInput();
}
void HandleGraphics() {
childImages[0].sprite = sparkSprites[SparkLevel % sparkSprites.Length];
childImages[1].sprite = flameSprites[SparkLevel / sparkSprites.Length];
}
void DebugHandleInput() {
if (Input.GetKeyDown("1")) {
SparkLevel--;
}
if (Input.GetKeyDown("2")) {
SparkLevel++;
}
if (Input.GetKeyDown("3")) {
FlameLevel--;
}
if (Input.GetKeyDown("4")) {
FlameLevel++;
}
}
}
@@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace MenuSystem {
public class ConfirmDelete : MenuOption {
//public access members
public string fileName;
public GameObject loadCanvas;
public GameObject confirmationCanvas;
public override void Execute() {
File.Delete(fileName);
confirmationCanvas.SetActive(false);
loadCanvas.SetActive(true);
}
public override void Scroll(float x) {
//DO NOTHING
}
}
}
@@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace MenuSystem {
public class ConfirmLoad : MenuOption {
//public access members
public SaveFileManager.SaveSlot saveData;
public string fileName;
void Update() {
if (transform.childCount > 0) {
//BUGFIX: I don't know why this is disabled here
transform.GetChild(0).GetComponent<Image>().enabled = true;
}
}
public override void Execute() {
SaveFileManager.saveSlotFileName = fileName;
SaveFileManager.LoadedSaveSlot = saveData;
SceneManager.LoadScene("Gameplay");
}
public override void Scroll(float x) {
//DO NOTHING
}
}
}
@@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
namespace MenuSystem {
public class LoadCanvas : MonoBehaviour {
//public access members
public GameObject saveSlotPrefab;
public GameObject confirmationCanvas;
void OnEnable() {
//load the save objects
DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath);
//create the load menu
foreach(var file in info.GetFiles("*.sav")) {
GameObject saveSlot = Instantiate(saveSlotPrefab) as GameObject;
saveSlot.transform.SetParent(transform); //BUGFIX: unity bug
saveSlot.GetComponent<SaveSlot>().SetSaveSlotInfo(SaveFileManager.LoadData(file.FullName));
saveSlot.GetComponent<SaveSlot>().fileName = file.FullName;
saveSlot.GetComponent<SaveSlot>().loadCanvas = transform.gameObject;
saveSlot.GetComponent<SaveSlot>().confirmationCanvas = confirmationCanvas;
}
//Move the "back" option to the bottom
transform.GetChild(0).SetAsLastSibling();
}
void OnDisable() {
foreach (Transform child in transform) {
if (child.GetComponent<SaveSlot>() != null) {
GameObject.Destroy(child.gameObject);
}
}
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
namespace MenuSystem {
public class SaveSlot : MenuOption {
public string fileName;
public GameObject loadCanvas;
public GameObject confirmationCanvas;
//private access members
SaveFileManager.SaveSlot saveData;
public void SetSaveSlotInfo(SaveFileManager.SaveSlot saveSlot) {
TextMeshProUGUI[] texts = GetComponentsInChildren<TextMeshProUGUI>(true);
texts[0].text = saveSlot.currentLocation;
TimeSpan time = TimeSpan.FromSeconds(saveSlot.secondsPlaying);
texts[1].text = time.ToString(@"hh\:mm\:ss");
saveData = saveSlot;
}
public override void Execute() {
//show a confirmation canvas
confirmationCanvas.transform.GetChild(0).GetComponent<ConfirmLoad>().saveData = saveData;
confirmationCanvas.transform.GetChild(0).GetComponent<ConfirmLoad>().fileName = fileName;
confirmationCanvas.transform.GetChild(1).GetComponent<ConfirmDelete>().fileName = fileName;
//BUGFIX: move the cursor somewhere safe
transform.Find("Cursor").SetParent(confirmationCanvas.transform.GetChild(0));
//BUGFIX: wait a frame to show the confirmation page, to make sure the cursor is safe
StartCoroutine(DoStuffNextFrame());
}
IEnumerator DoStuffNextFrame() {
yield return null;
loadCanvas.SetActive(false);
confirmationCanvas.SetActive(true);
}
public override void Scroll(float x) {
//DO NOTHING
}
}
}
@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MenuSystem {
class Quit : MenuOption {
override public void Execute() {
Application.Quit();
Debug.Log("Quit called");
}
}
}
@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace MenuSystem {
class StartGame : MenuOption {
override public void Execute() {
SceneManager.LoadScene("Gameplay");
}
}
}
+144
View File
@@ -0,0 +1,144 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace MenuSystem {
public class MenuCanvas : MonoBehaviour {
//public access members
public Image cursorImage;
public bool floating = false;
//private members
MenuOption[] menuOptions = null;
//fine-tuned input
const float deadZone = 0.25f;
const float inputDelay = 0.5f;
const float scrollDelay = 0.1f;
float lastInputX = float.NegativeInfinity;
float lastInputY = float.NegativeInfinity;
void OnEnable() {
menuOptions = GetComponentsInChildren<MenuOption>();
//set up the cursor
menuOptions[0].hover = true;
RectTransform rt = cursorImage.GetComponent<RectTransform>();
rt.SetParent(menuOptions[0].transform);
rt.offsetMin = new Vector2(0, 0);
rt.offsetMax = new Vector2(0, 0);
HandleGraphics();
}
void OnDisable() {
//BUGFIX: reset this menu tree if closing the menu
foreach (Transform child in transform) {
child.gameObject.SetActive(true);
}
}
void Update() {
//BUGFIX: remove all non-active elements
menuOptions = Array.FindAll(menuOptions, (MenuOption option) => option.gameObject.active);
HandleInput();
HandleGraphics();
}
void HandleInput() {
//press a button
if (GamePad.GetState().Pressed(CButton.A)) {
foreach(MenuOption option in menuOptions) {
if (option.hover) {
option.Execute();
}
}
return;
}
//scroll left or right
if (Mathf.Abs(GamePad.GetAxis(CAxis.LX)) >= deadZone && Time.unscaledTime - lastInputX > scrollDelay) {
lastInputX = Time.unscaledTime;
foreach(MenuOption option in menuOptions) {
if (option.hover) {
option.Scroll(GamePad.GetAxis(CAxis.LX));
}
}
return;
}
//keyboard/gamepad scroll up
if (GamePad.GetAxis(CAxis.LY) < -deadZone && Time.unscaledTime - lastInputY > inputDelay) {
lastInputY = Time.unscaledTime;
//move the cursor one up
for (int i = 1; i < menuOptions.Length; i++) {
//find the current hovered item
if (menuOptions[i].hover) {
//move the hover up
menuOptions[i].hover = false;
menuOptions[i-1].hover = true;
break;
}
}
}
//keyboard/gamepad scroll down
if (GamePad.GetAxis(CAxis.LY) > deadZone && Time.unscaledTime - lastInputY > inputDelay) {
lastInputY = Time.unscaledTime;
//move the cursor one down
for (int i = 0; i < menuOptions.Length - 1; i++) {
//find the current hovered item
if (menuOptions[i].hover) {
//move the hover down
menuOptions[i].hover = false;
menuOptions[i+1].hover = true;
break;
}
}
}
//deadzone reset lastInputX
if (Mathf.Abs(GamePad.GetAxis(CAxis.LX)) < deadZone) {
lastInputX = float.NegativeInfinity;
}
//deadzone reset lastInputY
if (Mathf.Abs(GamePad.GetAxis(CAxis.LY)) < deadZone) {
lastInputY = float.NegativeInfinity;
}
}
void HandleGraphics() {
bool hoverSet = false;
//update the graphics
foreach(MenuOption option in menuOptions) {
if (option.hover) {
if (hoverSet) {
option.hover = false;
continue;
}
RectTransform rt = cursorImage.GetComponent<RectTransform>();
rt.SetParent(option.transform);
rt.anchorMin = new Vector2(0, 0);
rt.anchorMax = new Vector2(1, 1);
rt.offsetMin = new Vector2(0, 0);
rt.offsetMax = new Vector2(0, 0);
hoverSet = true;
}
}
if (floating) {
float offset = cursorImage.transform.position.y - Screen.height / 2;
transform.position = new Vector2(transform.position.x, transform.position.y - offset);
}
}
}
}
@@ -0,0 +1,16 @@
using UnityEngine;
namespace MenuSystem {
public class MenuOption : MonoBehaviour {
//if this option is being hovered over, logically
public bool hover = false;
public virtual void Execute() {
//DO NOTHING
}
public virtual void Scroll(float x) {
//DO NOTHING
}
}
}
@@ -0,0 +1,37 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
namespace MenuSystem {
public class TextSpeed : MenuOption {
//component references
ConfigurationManager configManager;
TextMeshProUGUI text;
void Start() {
configManager = ConfigurationManager.Instance;
text = GetComponent<TextMeshProUGUI>();
configManager.textSpeed = (configManager.textSpeed != null && configManager.textSpeed != "" ? configManager.textSpeed : "Normal");
text.text = configManager.textSpeed;
}
override public void Execute() {
switch(configManager.textSpeed) {
case "Normal":
configManager.textSpeed = "Slow";
break;
case "Slow":
configManager.textSpeed = "Fast";
break;
case "Fast":
configManager.textSpeed = "Normal";
break;
}
text.text = configManager.textSpeed;
}
}
}
@@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
namespace MenuSystem {
public class Volume : MenuOption {
//component references
ConfigurationManager configManager;
TextMeshProUGUI text;
void Start() {
configManager = ConfigurationManager.Instance;
text = GetComponent<TextMeshProUGUI>();
configManager.volume = (configManager.volume != null && configManager.volume != 0 ? configManager.volume : 100f);
text.text = configManager.volume.ToString();
UpdateMasterVolume();
}
public override void Execute() {
//DO NOTHING
}
public override void Scroll(float x) {
//DO NOTHING
configManager.volume += x;
configManager.volume = Mathf.Clamp(configManager.volume, 0f, 100f);
text.text = configManager.volume.ToString();
UpdateMasterVolume();
}
void UpdateMasterVolume() {
AudioListener.volume = configManager.volume / 100f;
}
}
}
@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace MenuSystem {
public class QuitToMainMenu : MenuOption {
public override void Execute() {
SceneManager.LoadScene("MainMenu");
PauseManager.Instance.Paused = false;
}
}
}
@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MenuSystem {
public class ReturnToGame : MenuOption {
public override void Execute() {
PauseManager.Instance.Paused = false;
}
}
}
@@ -0,0 +1,17 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using TMPro;
namespace MenuSystem {
public class Heading : MonoBehaviour {
void Start() {
TextMeshProUGUI text = GetComponent<TextMeshProUGUI>();
if (!File.Exists(SaveFileManager.saveSlotFileName)) {
text.text = "Save The Game?";
}
}
}
}
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MenuSystem {
public class HideMeIf : MonoBehaviour {
public SaveHandler saveHandler;
void OnEnable() {
if (!saveHandler.saveMenuAvailable) {
gameObject.SetActive(false);
}
}
}
}
@@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MenuSystem {
public class SaveGame : MenuOption {
public SaveHandler saveHandler;
public Canvas oldMenu;
public Canvas newMenu;
public override void Execute() {
//calculate playtime
SaveFileManager.LoadedSaveSlot.secondsPlaying += Time.time - saveHandler.startTime;
saveHandler.startTime = Time.time;
//save the game
SaveFileManager.SaveData(SaveFileManager.LoadedSaveSlot, SaveFileManager.saveSlotFileName);
oldMenu.gameObject.SetActive(false);
newMenu.gameObject.SetActive(true);
}
public override void Scroll(float x) {
//DO NOTHING
}
}
}
@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace MenuSystem {
class SwitchMenus : MenuOption {
public Canvas oldMenu;
public Canvas newMenu;
override public void Execute() {
oldMenu.gameObject.SetActive(false);
newMenu.gameObject.SetActive(true);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Some files were not shown because too many files have changed in this diff Show More