initial commit

This commit is contained in:
Fred R
2022-04-05 02:09:50 +02:00
commit ae1a392435
512 changed files with 66150 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum AudioClip
{
CountdownBeep,
TurbineRoar,
TurbineSpin,
SnowSlideSledge,
Explosion,
EnemyHit,
ItemPickupEffect
}
[Serializable]
public struct AudioClipSource
{
public AudioClip Clip;
public AudioSource Source;
}
public class AudioManager : Manager<AudioManager>
{
[SerializeField]
private List<AudioClipSource> audioClipSources = new List<AudioClipSource>();
[SerializeField]
private float globalAudioVolume;
private Dictionary<AudioClip, AudioSource> audioSources = new Dictionary<AudioClip, AudioSource>();
public void SetGlobalAudioVolume(float volume)
{
globalAudioVolume = volume;
AudioListener.volume = globalAudioVolume;
}
public void OnGameStateChanged(GameState newState, GameState oldState)
{
if(newState == GameState.Play && UIManagerMenu.Instance != null)
{
SetGlobalAudioVolume(PersistantStorage.Instance.AudioVolume);
}
}
protected override void Awake()
{
base.Awake();
foreach(AudioClipSource audioClipSource in audioClipSources)
audioSources[audioClipSource.Clip] = audioClipSource.Source;
}
protected void Start()
{
GameStateManager.Instance.OnGameStateChangedEvent.AddListener(OnGameStateChanged);
}
protected void OnDestroy()
{
GameStateManager.Instance.OnGameStateChangedEvent.RemoveListener(OnGameStateChanged);
}
public AudioSource GetAudioSource(AudioClip audioClip)
{
AudioSource audioClipSource = audioSources[audioClip];
return audioClipSource;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58b6f4d775eb295438b1d71072624184
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : Manager<CameraFollow>
{
public GameObject FollowObject;
public GameObject FollowObjectGameOver;
public float DampingRotationFac;
public float DampingPositionFac;
public float DampingRotationFacGameOver;
public float DampingPositionFacGameOver;
private Vector3 targetLocation;
private Quaternion targetRotation;
public AnimationCurve EasingPositionCurve;
public AnimationCurve EasingRotationCurve;
public AnimationCurve EasingPositionCurveGameOver;
public AnimationCurve EasingRotationCurveGameOver;
public void OnGameStateChanged(GameState newState, GameState oldState)
{
if(newState == GameState.Play)
{
updateTarget();
updateCameraLerp(1.0f, 1.0f);
}
if(newState == GameState.GameOver)
{
}
}
protected void Start()
{
GameStateManager.Instance.OnGameStateChangedEvent.AddListener(OnGameStateChanged);
}
protected void OnDestroy()
{
GameStateManager.Instance.OnGameStateChangedEvent.RemoveListener(OnGameStateChanged);
}
private void updateCameraLerp(float lerpRot, float lerpPos)
{
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, targetRotation, lerpRot);
this.transform.position = Vector3.Lerp(this.transform.position, targetLocation, lerpPos);
}
private void updateTarget()
{
GameObject activeFollowTarget = GameStateManager.Instance.GameState == GameState.Play ? FollowObject : FollowObjectGameOver;
Quaternion lookRotation = Quaternion.identity;
lookRotation.SetLookRotation(Vector3.Scale(activeFollowTarget.transform.forward, new Vector3(1.0f, 0.0f, 1.0f)).normalized, Vector3.up);
targetLocation = activeFollowTarget.transform.position;
targetRotation = lookRotation;
}
protected void OnGameOverAnimationFinished()
{
Debug.Log("OnGameOverAnimationFinished OKAY GO GO GO ");
}
protected void FixedUpdate()
{
updateTarget();
float activeDampingRotationFactor = GameStateManager.Instance.GameState == GameState.Play ? DampingRotationFac : DampingRotationFacGameOver;
float activeDampingPositionFactor = GameStateManager.Instance.GameState == GameState.Play ? DampingPositionFac : DampingPositionFacGameOver;
float tRot = Mathf.Clamp(Mathf.Abs(Quaternion.Angle(this.transform.rotation, targetRotation)) * activeDampingRotationFactor, 0.0f, 1.0f);
float tPos = Mathf.Clamp((this.transform.position - targetLocation).magnitude * activeDampingPositionFactor, 0.0f, 1.0f);
AnimationCurve activeRotationCurve = GameStateManager.Instance.GameState == GameState.Play ? EasingRotationCurve : EasingRotationCurveGameOver;
AnimationCurve activePositionCurve = GameStateManager.Instance.GameState == GameState.Play ? EasingPositionCurve : EasingPositionCurveGameOver;
tRot = activeRotationCurve.Evaluate(tRot);
tPos = activePositionCurve.Evaluate(tPos);
updateCameraLerp(tRot, tPos);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd43ff6bd4fd4c54f94035559b4682f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

141
Assets/Scripts/Chunk.cs Normal file
View File

@@ -0,0 +1,141 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chunk : MonoBehaviour
{
private Mesh mesh;
private const int MESH_RESOLUTION_X = 64;
private const int MESH_RESOLUTION_Y = 64;
public const float CHUNK_SIZE = 64.0f;
private const float MESH_TILE_RES = CHUNK_SIZE / MESH_RESOLUTION_X;
private List<GameObject> localPooledPrefabs = new List<GameObject>();
protected void Awake()
{
mesh = new Mesh();
Vector3[] vertices = new Vector3[MESH_RESOLUTION_X * MESH_RESOLUTION_Y * 4];
int[] triangles = new int[MESH_RESOLUTION_X*MESH_RESOLUTION_Y*3*2];
int vertIndex = 0;
int triangleIndex = 0;
for(int x=0; x<MESH_RESOLUTION_X; x++)
{
for(int y=0; y<MESH_RESOLUTION_Y; y++)
{
vertices[vertIndex+0] = (new Vector3(x, 0.0f, y)) * MESH_TILE_RES;
vertices[vertIndex+1] = (new Vector3(x+1, 0.0f, y)) * MESH_TILE_RES;
vertices[vertIndex+2] = (new Vector3(x, 0.0f, y+1)) * MESH_TILE_RES;
vertices[vertIndex+3] = (new Vector3(x+1, 0.0f, y+1)) * MESH_TILE_RES;
triangles[triangleIndex+0] = vertIndex+2;
triangles[triangleIndex+1] = vertIndex+1;
triangles[triangleIndex+2] = vertIndex+0;
triangles[triangleIndex+3] = vertIndex+1;
triangles[triangleIndex+4] = vertIndex+2;
triangles[triangleIndex+5] = vertIndex+3;
vertIndex += 4;
triangleIndex += 6;
}
}
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.Optimize();
mesh.RecalculateNormals();
GetComponent<MeshFilter>().mesh = mesh;
MeshCollider meshCollider = GetComponent<MeshCollider>();
meshCollider.sharedMesh = mesh;
}
public void UpdateMesh()
{
Vector3[] vertices = mesh.vertices;
Vector3 vertex;
Vector3 worldSpaceVertexPosition;
for(int n=0;n<vertices.Length;n++)
{
vertex = vertices[n];
worldSpaceVertexPosition = this.transform.TransformPoint(vertex);
vertices[n] = new Vector3(vertex.x, TerrainGenerator.Instance.GetHeight(worldSpaceVertexPosition.x, worldSpaceVertexPosition.z), vertex.z);
}
mesh.vertices = vertices;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
}
public void UpdatePosition(Vector3 newPos)
{
this.transform.position = newPos;
foreach(GameObject g in localPooledPrefabs)
PoolManager.Instance.ReturnInstance(g);
localPooledPrefabs.Clear();
List<Vector2> points = PoissonDiscSampling.GeneratePoints(7.0f, new Vector2(CHUNK_SIZE, CHUNK_SIZE));
Vector2 globalPos = new Vector2(this.transform.position.x, this.transform.position.z);
foreach(Vector2 point in points)
{
Vector2 globalPoint = point + globalPos;
float noise_scale = 0.1f;
float noise = Mathf.PerlinNoise(1000.0f + globalPoint.x * noise_scale, 1000.0f + globalPoint.y * noise_scale) * 2.0f - 1.0f;
float distanceFromPath = TerrainGenerator.Instance.GetDistanceFromPath(globalPoint.x, globalPoint.y);
float height = TerrainGenerator.Instance.GetHeight(globalPoint.x, globalPoint.y);
if(height < 16.0f && noise > 0.01f && Mathf.Abs(distanceFromPath) > 1.0f)
{
GameObject g = PoolManager.Instance.GetInstance(TerrainGenerator.Instance.TreePrefab);
if(g != null)
{
g.transform.position = new Vector3(globalPoint.x, height, globalPoint.y);
localPooledPrefabs.Add(g);
}
}
}
if(this.transform.position.x == 0 && this.transform.position.z > 0)
{
int MAX_TREES_PATH = 8;
int additionalTrees = (int)Random.Range(0, MAX_TREES_PATH);
for(int n=0;n<additionalTrees;n++)
{
float yTreeOffset = n * (CHUNK_SIZE/MAX_TREES_PATH) + Random.Range(0.0f, (CHUNK_SIZE/MAX_TREES_PATH) * 0.25f) + this.transform.position.z;
float xTreeOffset = TerrainGenerator.Instance.GetPathCenter(yTreeOffset) * 15.0f + (Random.Range(-1.0f, 1.0f) * 10.0f);
Vector2 treePos = new Vector2(xTreeOffset, yTreeOffset);
float height = TerrainGenerator.Instance.GetHeight(treePos.x, treePos.y);
GameObject g = PoolManager.Instance.GetInstance(TerrainGenerator.Instance.SnowmanObstaclePrefab);
if(g != null)
{
g.transform.position = new Vector3(treePos.x, height, treePos.y);
g.transform.rotation = Quaternion.identity * Quaternion.AngleAxis(180.0f, Vector3.up);
localPooledPrefabs.Add(g);
}
}
int MAX_PICKUPITEMS_PATH = 1;
int pickupItemCount = (Random.Range(0.0f, 1.0f) * 1000.0f) > this.transform.position.z || Random.Range(0.0f, 1.0f) < 0.25f ? MAX_PICKUPITEMS_PATH : 0;
for(int n=0;n<pickupItemCount;n++)
{
float yItemOffset = n * (CHUNK_SIZE/MAX_PICKUPITEMS_PATH) + Random.Range(0.0f, (CHUNK_SIZE/MAX_PICKUPITEMS_PATH) * 0.75f + 0.5f ) + this.transform.position.z;
float xItemOffset = TerrainGenerator.Instance.GetPathCenter(yItemOffset) * 15.0f + (Random.Range(-1.0f, 1.0f) * 10.0f);
Vector2 itemPos = new Vector2(xItemOffset, yItemOffset);
float height = TerrainGenerator.Instance.GetHeight(itemPos.x, itemPos.y);
GameObject g = PoolManager.Instance.GetInstance(TerrainGenerator.Instance.PickupPrefab);
if(g != null)
{
g.transform.position = new Vector3(itemPos.x, height, itemPos.y);
localPooledPrefabs.Add(g);
}
}
}
this.UpdateMesh();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c734fd64010453842a7d416a72073eea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,170 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
public class GameStateChangedEvent : UnityEvent<GameState, GameState>
{
}
public enum GameState
{
Menu,
Play,
GameOver,
GameOverMenu
}
public class GameStateManager : PersistentManager<GameStateManager>
{
private bool isLoading;
private GameState gameState = GameState.Menu;
public GameState GameState { get { return gameState; } }
[SerializeField]
private Image TransitionImage;
private const float FADE_DURATION = 0.5f;
public GameStateChangedEvent OnGameStateChangedEvent = new GameStateChangedEvent();
public GameState InitialState;
protected override void Awake()
{
if(GameStateManager.Instance != null)
{
Destroy(this.gameObject);
}
else
{
base.Awake();
SetGameState(InitialState);
}
}
private IEnumerator FadeCanvasToBlackCoroutine()
{
if(TransitionImage != null)
{
float x = 0.0f;
while(x < 1.0f)
{
x += ((1.0f / FADE_DURATION) * Time.deltaTime);
if(x > 1.0f)
{
x = 1.0f;
}
Color c = TransitionImage.color;
c.a = x;
TransitionImage.color = c;
yield return new WaitForEndOfFrame();
}
}
}
private IEnumerator FadeCanvasToTransparentCoroutine()
{
if(TransitionImage != null)
{
float x = 0.0f;
while(x < 1.0f)
{
x += ((1.0f / FADE_DURATION) * Time.deltaTime);
if(x > 1.0f)
{
x = 1.0f;
}
Color c = TransitionImage.color;
c.a = x * -1.0f;
TransitionImage.color = c;
yield return new WaitForEndOfFrame();
}
}
}
private IEnumerator loadGameSceneCoroutine()
{
StartCoroutine(FadeCanvasToBlackCoroutine());
yield return new WaitForSeconds(FADE_DURATION);
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("test");
while(!asyncOperation.isDone)
{
yield return new WaitForSeconds(0.1f);
}
yield return new WaitForSeconds(1.0f);
this.updateGameStateAndFireEvents(GameState.Play);
StartCoroutine(FadeCanvasToTransparentCoroutine());
isLoading = false;
}
private void updateGameStateAndFireEvents(GameState newState)
{
GameState oldGameStae = this.gameState;
this.gameState = newState;
OnGameStateChangedEvent.Invoke(this.gameState, oldGameStae);
}
private IEnumerator playGameOverAnimationAndSwitchScene()
{
this.updateGameStateAndFireEvents(GameState.GameOver);
yield return new WaitForSeconds(1.5f);
AudioManager.Instance.GetAudioSource(AudioClip.Explosion).Play();
Player.Instance.Explosion.Play();
yield return new WaitForSeconds(0.4f);
PersistantStorage.Instance.Score = Player.Instance.Score;
this.SetGameState(GameState.GameOverMenu);
}
public void SetGameState(GameState gameState)
{
if(gameState == GameState.Play)
{
if(!isLoading)
{
isLoading = true;
StartCoroutine(loadGameSceneCoroutine());
}
}
if(gameState == GameState.GameOver)
{
StartCoroutine(playGameOverAnimationAndSwitchScene());
}
if(gameState == GameState.GameOverMenu)
{
SceneManager.LoadScene("GameOverMenu");
this.updateGameStateAndFireEvents(GameState.GameOverMenu);
}
if(gameState == GameState.Menu)
{
SceneManager.LoadScene("Menu");
this.updateGameStateAndFireEvents(GameState.Menu);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 27925c0f43ebe99409c7f18bd0c25843
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameStateManagerEditor : GameStateManager
{
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8cbab65ffe7eede4abec58caf5e5458e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

22
Assets/Scripts/Manager.cs Normal file
View File

@@ -0,0 +1,22 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Manager<TManager> : MonoBehaviour
where TManager : Component
{
private static TManager instance;
public static TManager Instance
{
get
{
return instance;
}
}
protected virtual void Awake()
{
instance = this as TManager;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c9bd6920fe8e254c8edc1c99e018e13
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Obstacle : PoolableMonoBehaviour
{
[SerializeField]
private bool destroyOnHit = false;
[SerializeField]
private float damageOnHit = 0.1f;
private AudioSource audioSource;
[SerializeField]
private float hitRecoilForce = 1.0f;
private Dictionary<GameObject, Vector3> originalPosition = new Dictionary<GameObject, Vector3>();
private Dictionary<GameObject, Quaternion> originalRotation = new Dictionary<GameObject, Quaternion>();
public float DamageOnHit => damageOnHit;
public bool DestroyOnHit => destroyOnHit;
public float HitRecoilForce => hitRecoilForce;
protected void Start()
{
audioSource = GetComponent<AudioSource>();
foreach(Transform t in GetComponentsInChildren<Transform>().Where(x => x.gameObject != this.gameObject))
{
originalPosition[t.gameObject] = t.localPosition;
originalRotation[t.gameObject] = t.localRotation;
}
}
public void OnHit(GameObject other)
{
if(destroyOnHit)
{
foreach(Rigidbody r in GetComponentsInChildren<Rigidbody>().Where(x => x.gameObject != this.gameObject))
r.isKinematic = false;
this.GetComponent<Collider>().enabled = false;
}
if(audioSource != null)
audioSource.Play();
}
public override void OnReturn()
{
foreach(Rigidbody r in this.GetComponentsInChildren<Rigidbody>())
r.isKinematic = true;
foreach(GameObject g in this.GetComponentsInChildren<Transform>().Where(x => x.gameObject != this.gameObject).Select(x => x.gameObject))
{
g.transform.localPosition = originalPosition[g];
g.transform.localRotation = originalRotation[g];
}
this.GetComponent<Collider>().enabled = true;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f9fb24ac82dd454bb27136d4d9f08af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PersistantStorage : PersistentManager<PersistantStorage>
{
public float AudioVolume;
public float Score;
public bool enableNsfwMode = false;
protected override void Awake()
{
if(Instance != null)
{
Debug.Log("Deleting PersistantStorage cause one is already in your Scene.");
Destroy(this.gameObject);
return;
}
base.Awake();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49a229d6933e8eb45a787c7156aceac5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PersistentManager<TManager> : Manager<TManager>
where TManager : Component
{
protected override void Awake()
{
DontDestroyOnLoad(this.gameObject);
base.Awake();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5cd81f2f13fe70249a6344ab69410306
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickupItem : PoolableMonoBehaviour
{
public float Fuel
{
get
{
if(PickedUp)
return 0.0f;
return 0.25f;
}
}
public GameObject WavingPreviewItem;
public bool PickedUp = false;
public override void OnReturn()
{
PickedUp = false;
WavingPreviewItem.SetActive(true);
base.OnReturn();
}
public void OnPickup()
{
PickedUp = true;
WavingPreviewItem.SetActive(false);
AudioManager.Instance.GetAudioSource(AudioClip.ItemPickupEffect).Play();
}
protected void Update()
{
WavingPreviewItem.transform.localPosition = new Vector3(0.0f, Mathf.Sin(Time.time * 3.0f) * 0.15f, 0.0f);
WavingPreviewItem.transform.localRotation = Quaternion.AngleAxis(100.0f * Time.deltaTime, Vector3.up) * WavingPreviewItem.transform.localRotation;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc45bdb42ebc7ab4b9004dd2cf4bf358
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

275
Assets/Scripts/Player.cs Normal file
View File

@@ -0,0 +1,275 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : Manager<Player>
{
public float ThrustForce = 10; // in m/s^2
public float SteeringFactor = 10.0f;
public float UprightMultiplier = 10.0f;
public float DragForce = 0.75f;
public AnimationCurve SlopeDragCurve;
public Vector3 horizontalVelocity = Vector2.zero;
private BoxCollider BenchBoxCollider;
[SerializeField]
private ParticleSystem thrusterParticleSystemLeft;
[SerializeField]
private ParticleSystem thrusterParticleSystemRight;
public ParticleSystem Explosion;
[SerializeField]
private float dynamiteCountdownStart = 3.0f;
private float dynamiteCountDown;
private bool disableInput = false;
public float DynamiteCountDown => dynamiteCountDown;
public float Fuel = 1.0f;
public float FuelConsumptionPerSecond = 0.01f;
public float FuelConsumptionPerSecondBase = 0.01f;
private float score = 0.0f;
public float Score => score;
public LayerMask RayLayerMask;
IEnumerator SecondDelayInterval()
{
while(true)
{
yield return new WaitForSeconds(1.0f);
if(dynamiteCountDown > 0)
{
if(horizontalVelocity.magnitude < 0.1f)
{
dynamiteCountDown -= 1.0f;
AudioManager.Instance.GetAudioSource(AudioClip.CountdownBeep).Play();
}
else
{
dynamiteCountDown = dynamiteCountdownStart;
}
}
if(dynamiteCountDown <= 0)
{
disableInput = true;
dynamiteCountDown = 0;
if(GameStateManager.Instance.GameState == GameState.Play)
{
Debug.Log("GameStateChange => Play => GameOver");
GameStateManager.Instance.SetGameState(GameState.GameOver);
}
}
}
}
private AudioSource audioSourceSledge;
private AudioSource audioSourceSpin;
private AudioSource audioSourceRoar;
protected void Start()
{
GameStateManager.Instance.OnGameStateChangedEvent.AddListener(OnGameStateChanged);
BenchBoxCollider = GetComponentInChildren<BoxCollider>();
dynamiteCountDown = dynamiteCountdownStart;
audioSourceSpin = AudioManager.Instance.GetAudioSource(AudioClip.TurbineSpin);
audioSourceRoar = AudioManager.Instance.GetAudioSource(AudioClip.TurbineRoar);
audioSourceSledge = AudioManager.Instance.GetAudioSource(AudioClip.SnowSlideSledge);
}
public void OnGameStateChanged(GameState newState, GameState oldState)
{
Debug.Log("OnGameStateChanged "+newState);
if(newState == GameState.Play)
{
StartCoroutine(SecondDelayInterval());
}
}
protected void OnDestroy()
{
GameStateManager.Instance.OnGameStateChangedEvent.RemoveListener(OnGameStateChanged);
}
void FixedUpdate()
{
var gamepad = Gamepad.current;
float throttle = 0;
float steering = 0;
Vector3 localVelocity = this.transform.InverseTransformDirection(horizontalVelocity);
if(!disableInput)
{
throttle += (Keyboard.current.wKey.ReadValue()) + (Keyboard.current.sKey.ReadValue() * -1.0f);
steering += (Keyboard.current.dKey.ReadValue()) + (Keyboard.current.aKey.ReadValue() * -1.0f);
if(gamepad != null)
{
float throttleR = gamepad.rightTrigger.ReadValue();
float throttleL = gamepad.leftTrigger.ReadValue() * -1.0f;
throttle += throttleL + throttleR;
steering += gamepad.leftStick.ReadValue().x;
if(Vector3.Dot(Vector3.forward, localVelocity) < -0.1f)
steering *= -1.0f;
}
}
audioSourceRoar.volume = Mathf.Abs(throttle) * 0.75f;
audioSourceRoar.pitch = 1.05f;
audioSourceSledge.volume = Mathf.Clamp(horizontalVelocity.magnitude, 0.0f, 1.0f);
Fuel -= (Mathf.Abs(throttle) * FuelConsumptionPerSecond + FuelConsumptionPerSecondBase) * Time.fixedDeltaTime;
if(Fuel <= 0)
{
Fuel = 0;
disableInput = true;
}
if(disableInput)
{
audioSourceSpin.volume = 0.0f;
audioSourceRoar.volume = 0.0f;
}
float baseEmission = disableInput ? 0 : 0.1f;
thrusterParticleSystemLeft.emissionRate = (baseEmission + (1.0f - baseEmission) * (throttle - steering * -1.0f)) * 300.0f;
thrusterParticleSystemRight.emissionRate = (baseEmission + (1.0f - baseEmission) * (throttle - steering)) * 300.0f;
this.transform.Rotate(this.transform.up, steering * SteeringFactor * Time.fixedDeltaTime, Space.World);
Vector3 forward = this.transform.forward;
Vector3 horizontalForward = Vector3.Scale(new Vector3(1.0f, 0.0f, 1.0f), forward);
score = Mathf.Round(this.transform.position.z * 1000.0f) / 1000.0f;
horizontalVelocity += this.transform.forward * throttle * ThrustForce * Time.fixedDeltaTime;
horizontalVelocity -= horizontalVelocity * DragForce * Time.fixedDeltaTime;
UIManagerGame.Instance.Fuel = Fuel;
UIManagerGame.Instance.CountdownSeconds = (int)dynamiteCountDown;
UIManagerGame.Instance.Score = score;
this.transform.position += horizontalVelocity;
float OFFSET = 0.5f;
int OFFSET_COUNT_X = 4;
int OFFSET_COUNT_Y = 4;
// TODO: Find better way to smoothly slide across the terrain without bump distortions
int TOTAL_OFFSET_COUNT = OFFSET_COUNT_X * OFFSET_COUNT_Y;
Vector3[] floorRaycastOffsets = new Vector3[TOTAL_OFFSET_COUNT];
for(int x=0;x<OFFSET_COUNT_X; x++)
{
for(int y=0;y<OFFSET_COUNT_Y; y++)
{
float offsetX = ((2.0f / (OFFSET_COUNT_X-1)) * x) - 1.0f;
float offsetY = ((2.0f / (OFFSET_COUNT_Y-1)) * y) - 1.0f;
Vector3 offset = Vector3.up * OFFSET + Vector3.Scale((BenchBoxCollider.size / 2), new Vector3(offsetX, 0.0f, offsetY));
floorRaycastOffsets[x*OFFSET_COUNT_X+y] = offset;
}
}
Vector3[] floorRaycastHitOffsets = new Vector3[TOTAL_OFFSET_COUNT];
Vector3 averageNormal = Vector3.zero;
Vector3 averagePosition = Vector3.zero;
int hitCount = 0;
for(int n=0;n<floorRaycastHitOffsets.Length;n++)
{
Vector3 offset = floorRaycastOffsets[n];
RaycastHit hitInfo;
bool raycastHit = Physics.Raycast(this.transform.TransformPoint(offset), -this.transform.up, out hitInfo, 1000.0f, RayLayerMask.value);
if(raycastHit)
{
hitCount++;
floorRaycastHitOffsets[n] = hitInfo.point;
averageNormal += hitInfo.normal;
averagePosition += hitInfo.point;
}
}
averageNormal *= (1.0f / hitCount);
averagePosition *= (1.0f / hitCount);
if(hitCount == 0)
{
return;
}
Vector3 right = Vector3.Cross(averageNormal, this.transform.forward);
Quaternion rot = Quaternion.AngleAxis(90.0f, right);
Quaternion newRot = this.transform.rotation;
newRot.SetLookRotation(rot * averageNormal, averageNormal);
this.transform.rotation = newRot;
this.transform.position = averagePosition + this.transform.up * 1.0f;
Vector3 slope = Quaternion.AngleAxis(90.0f, Vector3.Cross(Vector3.up, averageNormal).normalized) * averageNormal;
float slopeMagnitude = Vector3.Scale(averageNormal, new Vector3(1.0f, 0.0f, 1.0f)).magnitude;
horizontalVelocity += slope.normalized * Physics.gravity.magnitude * Time.fixedDeltaTime * 0.1f * SlopeDragCurve.Evaluate(slopeMagnitude);
Debug.DrawRay(this.transform.position, slope, Color.yellow);
Debug.DrawRay(this.transform.position, horizontalForward, Color.green);
Debug.DrawRay(this.transform.position, rot * averageNormal, Color.red);
Debug.DrawRay(this.transform.position, averageNormal, Color.blue);
Debug.DrawRay(this.transform.position, right, Color.magenta);
}
protected void OnTriggerEnter(Collider collider)
{
Obstacle o = collider.gameObject.GetComponent<Obstacle>();
if(o != null)
{
o.OnHit(this.gameObject);
if(o.HitRecoilForce != 0.0f)
{
this.horizontalVelocity += -(collider.transform.position - this.transform.position).normalized * o.HitRecoilForce;
}
this.Fuel -= o.DamageOnHit;
}
PickupItem pu = collider.gameObject.GetComponent<PickupItem>();
if(pu != null && pu.PickedUp == false)
{
this.Fuel += pu.Fuel;
if(this.Fuel > 1.0f)
this.Fuel = 1.0f;
pu.OnPickup();
if(disableInput)
disableInput = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6651821cb84595a4e85ac33c17026b76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class PoissonDiscSampling {
public static List<Vector2> GeneratePoints(float radius, Vector2 sampleRegionSize, int numSamplesBeforeRejection = 30) {
float cellSize = radius/Mathf.Sqrt(2);
int[,] grid = new int[Mathf.CeilToInt(sampleRegionSize.x/cellSize), Mathf.CeilToInt(sampleRegionSize.y/cellSize)];
List<Vector2> points = new List<Vector2>();
List<Vector2> spawnPoints = new List<Vector2>();
spawnPoints.Add(sampleRegionSize/2);
while (spawnPoints.Count > 0) {
int spawnIndex = Random.Range(0,spawnPoints.Count);
Vector2 spawnCentre = spawnPoints[spawnIndex];
bool candidateAccepted = false;
for (int i = 0; i < numSamplesBeforeRejection; i++)
{
float angle = Random.value * Mathf.PI * 2;
Vector2 dir = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle));
Vector2 candidate = spawnCentre + dir * Random.Range(radius, 2*radius);
if (IsValid(candidate, sampleRegionSize, cellSize, radius, points, grid)) {
points.Add(candidate);
spawnPoints.Add(candidate);
grid[(int)(candidate.x/cellSize),(int)(candidate.y/cellSize)] = points.Count;
candidateAccepted = true;
break;
}
}
if (!candidateAccepted) {
spawnPoints.RemoveAt(spawnIndex);
}
}
return points;
}
static bool IsValid(Vector2 candidate, Vector2 sampleRegionSize, float cellSize, float radius, List<Vector2> points, int[,] grid) {
if (candidate.x >=0 && candidate.x < sampleRegionSize.x && candidate.y >= 0 && candidate.y < sampleRegionSize.y) {
int cellX = (int)(candidate.x/cellSize);
int cellY = (int)(candidate.y/cellSize);
int searchStartX = Mathf.Max(0,cellX -2);
int searchEndX = Mathf.Min(cellX+2,grid.GetLength(0)-1);
int searchStartY = Mathf.Max(0,cellY -2);
int searchEndY = Mathf.Min(cellY+2,grid.GetLength(1)-1);
for (int x = searchStartX; x <= searchEndX; x++) {
for (int y = searchStartY; y <= searchEndY; y++) {
int pointIndex = grid[x,y]-1;
if (pointIndex != -1) {
float sqrDst = (candidate - points[pointIndex]).sqrMagnitude;
if (sqrDst < radius*radius) {
return false;
}
}
}
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88bf0d25ff83d0c44b22be406d664124
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public struct InstantiationPrefabSettings
{
public GameObject prefab;
public int minCount;
public int maxCount;
}
public class Pool
{
private InstantiationPrefabSettings settings;
private Queue<GameObject> remainingGameObjects = new Queue<GameObject>();
private HashSet<GameObject> instancedGameObjects = new HashSet<GameObject>();
public Pool(InstantiationPrefabSettings settings)
{
this.settings = settings;
}
public void Init()
{
remainingGameObjects = new Queue<GameObject>();
instancedGameObjects = new HashSet<GameObject>();
for(int n=0;n<settings.minCount;n++)
{
GameObject g = GameObject.Instantiate(settings.prefab, Vector3.zero, Quaternion.identity);
g.SetActive(false);
g.transform.parent = PoolManager.Instance.transform;
remainingGameObjects.Enqueue(g);
}
}
public GameObject GetInstance()
{
int remainingInstanceCount = remainingGameObjects.Count;
int remainingInstantiableCount = settings.maxCount - (remainingGameObjects.Count + instancedGameObjects.Count);
GameObject instance = null;
if(remainingInstanceCount > 0)
{
instance = remainingGameObjects.Dequeue();
instance.SetActive(true);
instancedGameObjects.Add(instance);
}
else if(remainingInstantiableCount > 0)
{
instance = GameObject.Instantiate(settings.prefab, Vector3.zero, Quaternion.identity);
instancedGameObjects.Add(instance);
}
if(instance != null)
{
instance.transform.parent = null;
PoolableMonoBehaviour poolableMonoBehaviour = instance.GetComponent<PoolableMonoBehaviour>();
if(poolableMonoBehaviour != null)
{
poolableMonoBehaviour.OnRetrieve();
}
}
return instance;
}
public void ReturnInstance(GameObject gameObj)
{
int remainingInstanceCount = remainingGameObjects.Count;
if(remainingInstanceCount < settings.minCount)
{
PoolableMonoBehaviour poolableMonoBehaviour = gameObj.GetComponent<PoolableMonoBehaviour>();
if(poolableMonoBehaviour != null)
{
poolableMonoBehaviour.OnReturn();
}
gameObj.SetActive(false);
gameObj.transform.parent = PoolManager.Instance.transform;
remainingGameObjects.Enqueue(gameObj);
}
else
{
GameObject.Destroy(gameObj);
}
}
}
public class PoolManager : MonoBehaviour
{
public InstantiationPrefabSettings[] InstantiableGameObjects = new InstantiationPrefabSettings[0];
private Dictionary<GameObject, Pool> pools = new Dictionary<GameObject, Pool>();
private Dictionary<GameObject, GameObject> instanceToPrefabDict = new Dictionary<GameObject, GameObject>();
private static PoolManager instance;
public static PoolManager Instance
{
get
{
return instance;
}
}
protected void Awake()
{
instance = this;
}
protected void Start()
{
foreach(InstantiationPrefabSettings poolInstantiationSettings in InstantiableGameObjects)
{
Pool pool = new Pool(poolInstantiationSettings);
pools[poolInstantiationSettings.prefab] = pool;
pool.Init();
}
}
public GameObject GetInstance(GameObject prefab)
{
GameObject instance = pools[prefab].GetInstance();
if(instance != null)
{
instanceToPrefabDict[instance] = prefab;
}
return instance;
}
public void ReturnInstance(GameObject gameObj)
{
GameObject prefab = instanceToPrefabDict[gameObj];
pools[prefab].ReturnInstance(gameObj);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24b24e32684da184492e79dcbcc8ee6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolableMonoBehaviour : MonoBehaviour
{
public virtual void OnReturn()
{
}
public virtual void OnRetrieve()
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e70d72831eb055b489fbff47f198f54a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SnowmanInitializer : MonoBehaviour
{
[SerializeField]
private GameObject noseNSFW;
[SerializeField]
private GameObject noseNormal;
void Start()
{
if(!PersistantStorage.Instance.enableNsfwMode)
{
noseNSFW.SetActive(false);
}
else
{
noseNormal.SetActive(false);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b673cbaca655a624fa57ed179ca18cad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TerrainGenerator : Manager<TerrainGenerator>
{
public int ChunkRadiusX = 1;
public int ChunkRadiusY = 10;
public GameObject ChunkPrefab;
public GameObject TreePrefab;
public GameObject SnowmanObstaclePrefab;
public GameObject PickupPrefab;
public AnimationCurve TrenchProfile;
private Dictionary<Vector2Int, Chunk> chunks = new Dictionary<Vector2Int, Chunk>();
private const int LOAD_PER_UPDATE = 1;
public void FixedUpdate()
{
HashSet<Vector2Int> loadedPositions = new HashSet<Vector2Int>();
for(int x=-ChunkRadiusX;x<=ChunkRadiusX;x++)
{
for(int y=-ChunkRadiusY;y<=ChunkRadiusY;y++)
{
Vector2Int localChunkPos = new Vector2Int(x, y);
Vector3 playerGlobalPos = Player.Instance.transform.position;
Vector2Int playerPos = new Vector2Int((int)(playerGlobalPos.x / Chunk.CHUNK_SIZE), (int)(playerGlobalPos.z / Chunk.CHUNK_SIZE));
Vector2Int globalChunkPos = playerPos + localChunkPos;
loadedPositions.Add(globalChunkPos);
}
}
List<KeyValuePair<Vector2Int, Chunk>> unload = chunks.Where(x => !loadedPositions.Contains(x.Key)).ToList();
foreach(KeyValuePair<Vector2Int, Chunk> instance in unload)
{
PoolManager.Instance.ReturnInstance(instance.Value.gameObject);
chunks.Remove(instance.Key);
}
List<Vector2Int> load = loadedPositions.Where(x => !chunks.ContainsKey(x)).ToList();
if(GameStateManager.Instance.GameState == GameState.Play)
load = load.Take(LOAD_PER_UPDATE).ToList();
foreach(Vector2Int pos in load)
{
Chunk c = PoolManager.Instance.GetInstance(ChunkPrefab).GetComponent<Chunk>();
c.UpdatePosition(Vector3.Scale(Vector3.one * Chunk.CHUNK_SIZE, new Vector3(pos.x, 0.0f, pos.y)));
chunks[pos] = c;
}
}
public const float noise_seed = 10000.0f;
public float GetPathCenter(float y)
{
float pathwayNoiseScale = 0.025f;
float pathwayBlockingNoiseLayer = (Mathf.PerlinNoise(0.5f, noise_seed + y * pathwayNoiseScale) * 2.0f) - 1.0f;
return pathwayBlockingNoiseLayer;
}
public float GetDistanceFromPath(float x, float y)
{
float distance = ((x/15.0f) - GetPathCenter(y));
return distance;
}
public float GetHeight(float x, float y)
{
float x_pow_2 = x * x;
float x_pow_3 = x * x * x;
float x_pow_4 = x * x * x * x;
float baseHeight = 0.000001f * x_pow_4;
float noise_scale_0 = 0.05f;
float noise_0 = Mathf.PerlinNoise(noise_seed + x * noise_scale_0, noise_seed + y * noise_scale_0) * 2.0f - 1.0f;
float noise_scale_1 = 0.001f;
float noise_1 = Mathf.PerlinNoise(noise_seed + x * noise_scale_1, noise_seed + y * noise_scale_1) * 2.0f - 1.0f;
float noise_scale_2 = 0.001f;
float noise_2 = Mathf.PerlinNoise(noise_seed + x * noise_scale_2, noise_seed + y * noise_scale_2) * 2.0f - 1.0f;
float distance = GetDistanceFromPath(x, y);
float height = baseHeight + (noise_0 * 5.0f) * TrenchProfile.Evaluate(distance) + noise_1 * 5.0f + noise_2 * 10.0f;
return height + ((TrenchProfile.Evaluate(distance)-1.0f)*3.0f);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1cdd0ae8d46a5ea448bb5e320cba6aeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class UIManagerGame : Manager<UIManagerGame>
{
public Button FuelIndicator;
public float Fuel = 1.0f;
public int CountdownSeconds = 0;
public float Score = 0.0f;
private RectTransform fuelIndicatorRect;
private Vector2 initialFuelIndicatorSize;
[SerializeField]
private TextMeshProUGUI countdownText;
[SerializeField]
private TextMeshProUGUI scoreText;
private CanvasRenderer canvasRenderer;
public void OnGameStateChanged(GameState newState, GameState oldState)
{
if(newState == GameState.Play)
{
this.gameObject.SetActive(true);
}
else
{
this.gameObject.SetActive(false);
}
}
protected void Start()
{
GameStateManager.Instance.OnGameStateChangedEvent.AddListener(OnGameStateChanged);
canvasRenderer = GetComponent<CanvasRenderer>();
canvasRenderer.SetAlpha(1.0f);
fuelIndicatorRect = (RectTransform)FuelIndicator.transform;
initialFuelIndicatorSize = fuelIndicatorRect.sizeDelta;
}
protected void Update()
{
fuelIndicatorRect.sizeDelta = new Vector2(Fuel * initialFuelIndicatorSize.x, initialFuelIndicatorSize.y);
int countdownSeconds = CountdownSeconds%60;
int countdownMinutes = CountdownSeconds/60;
countdownText.text = (countdownMinutes < 10 ? "0" : "") + countdownMinutes + ":" + (countdownSeconds < 10 ? "0" : "") + countdownSeconds;
scoreText.text = Score + "m";
}
protected void OnDestroy()
{
GameStateManager.Instance.OnGameStateChangedEvent.RemoveListener(OnGameStateChanged);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7744e75ad8fe60748ba7c66e7c265965
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class UIManagerMenu : Manager<UIManagerMenu>
{
[SerializeField]
private Slider audioVolumeSlider;
[SerializeField]
private Button playButton;
private void OnGameStateChanged(GameState newState, GameState oldState)
{
if(newState == GameState.Play)
{
this.gameObject.SetActive(false);
}
if(newState == GameState.Menu)
{
this.gameObject.SetActive(true);
}
}
protected void Start()
{
GameStateManager.Instance.OnGameStateChangedEvent.AddListener(this.OnGameStateChanged);
playButton.onClick.AddListener(OnPlayButtonClicked);
audioVolumeSlider.value = PersistantStorage.Instance.AudioVolume;
}
protected void Update()
{
PersistantStorage.Instance.AudioVolume = audioVolumeSlider.value;
}
protected void OnDestroy()
{
GameStateManager.Instance.OnGameStateChangedEvent.RemoveListener(this.OnGameStateChanged);
}
private void OnPlayButtonClicked()
{
GameStateManager.Instance.SetGameState(GameState.Play);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17fdf04deb4f70f4f9b2b64236d02439
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class UIMangerGameOverScreen : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI textScore;
[SerializeField]
private Button mainMenuButton;
private void Start()
{
mainMenuButton.onClick.AddListener(OnMainMenuButtonClicked);
}
void Update()
{
textScore.text = $"Score: {PersistantStorage.Instance.Score}m";
}
public void OnMainMenuButtonClicked()
{
GameStateManager.Instance.SetGameState(GameState.Menu);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b52c65c02847794190e5b83efaded47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: