initial commit
This commit is contained in:
71
Assets/Scripts/AudioManager.cs
Normal file
71
Assets/Scripts/AudioManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/AudioManager.cs.meta
Normal file
11
Assets/Scripts/AudioManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58b6f4d775eb295438b1d71072624184
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
88
Assets/Scripts/CameraFollow.cs
Normal file
88
Assets/Scripts/CameraFollow.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/CameraFollow.cs.meta
Normal file
11
Assets/Scripts/CameraFollow.cs.meta
Normal 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
141
Assets/Scripts/Chunk.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Chunk.cs.meta
Normal file
11
Assets/Scripts/Chunk.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c734fd64010453842a7d416a72073eea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
170
Assets/Scripts/GameStateManager.cs
Normal file
170
Assets/Scripts/GameStateManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/GameStateManager.cs.meta
Normal file
11
Assets/Scripts/GameStateManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27925c0f43ebe99409c7f18bd0c25843
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/GameStateManagerEditor.cs
Normal file
8
Assets/Scripts/GameStateManagerEditor.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class GameStateManagerEditor : GameStateManager
|
||||
{
|
||||
|
||||
}
|
||||
11
Assets/Scripts/GameStateManagerEditor.cs.meta
Normal file
11
Assets/Scripts/GameStateManagerEditor.cs.meta
Normal 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
22
Assets/Scripts/Manager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Manager.cs.meta
Normal file
11
Assets/Scripts/Manager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c9bd6920fe8e254c8edc1c99e018e13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/Scripts/Obstacle.cs
Normal file
66
Assets/Scripts/Obstacle.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Obstacle.cs.meta
Normal file
11
Assets/Scripts/Obstacle.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f9fb24ac82dd454bb27136d4d9f08af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/Scripts/PersistantStorage.cs
Normal file
21
Assets/Scripts/PersistantStorage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PersistantStorage.cs.meta
Normal file
11
Assets/Scripts/PersistantStorage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49a229d6933e8eb45a787c7156aceac5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/PersistentManager.cs
Normal file
14
Assets/Scripts/PersistentManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PersistentManager.cs.meta
Normal file
11
Assets/Scripts/PersistentManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cd81f2f13fe70249a6344ab69410306
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Scripts/PickupItem.cs
Normal file
42
Assets/Scripts/PickupItem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PickupItem.cs.meta
Normal file
11
Assets/Scripts/PickupItem.cs.meta
Normal 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
275
Assets/Scripts/Player.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Player.cs.meta
Normal file
11
Assets/Scripts/Player.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6651821cb84595a4e85ac33c17026b76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/Scripts/PoissonDiscSampling.cs
Normal file
66
Assets/Scripts/PoissonDiscSampling.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PoissonDiscSampling.cs.meta
Normal file
11
Assets/Scripts/PoissonDiscSampling.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88bf0d25ff83d0c44b22be406d664124
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
140
Assets/Scripts/PoolManager.cs
Normal file
140
Assets/Scripts/PoolManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PoolManager.cs.meta
Normal file
11
Assets/Scripts/PoolManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24b24e32684da184492e79dcbcc8ee6e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Assets/Scripts/PoolableMonoBehaviour.cs
Normal file
16
Assets/Scripts/PoolableMonoBehaviour.cs
Normal 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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/PoolableMonoBehaviour.cs.meta
Normal file
11
Assets/Scripts/PoolableMonoBehaviour.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e70d72831eb055b489fbff47f198f54a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/Scripts/SnowmanInitializer.cs
Normal file
25
Assets/Scripts/SnowmanInitializer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/SnowmanInitializer.cs.meta
Normal file
11
Assets/Scripts/SnowmanInitializer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b673cbaca655a624fa57ed179ca18cad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
99
Assets/Scripts/TerrainGenerator.cs
Normal file
99
Assets/Scripts/TerrainGenerator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/TerrainGenerator.cs.meta
Normal file
11
Assets/Scripts/TerrainGenerator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cdd0ae8d46a5ea448bb5e320cba6aeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/Scripts/UIManagerGame.cs
Normal file
61
Assets/Scripts/UIManagerGame.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIManagerGame.cs.meta
Normal file
11
Assets/Scripts/UIManagerGame.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7744e75ad8fe60748ba7c66e7c265965
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
51
Assets/Scripts/UIManagerMenu.cs
Normal file
51
Assets/Scripts/UIManagerMenu.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIManagerMenu.cs.meta
Normal file
11
Assets/Scripts/UIManagerMenu.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17fdf04deb4f70f4f9b2b64236d02439
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
33
Assets/Scripts/UIMangerGameOverScreen.cs
Normal file
33
Assets/Scripts/UIMangerGameOverScreen.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/UIMangerGameOverScreen.cs.meta
Normal file
11
Assets/Scripts/UIMangerGameOverScreen.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b52c65c02847794190e5b83efaded47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user