基础内容
必要插件安装 缓动曲线和动画基础 ElementFolder,Track与其次级模块,PathNode重构
This commit is contained in:
255
Assets/Feel/FeelDemos/Snake/Scripts/Snake.cs
Normal file
255
Assets/Feel/FeelDemos/Snake/Scripts/Snake.cs
Normal file
@@ -0,0 +1,255 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Feedbacks;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
#if MM_UI
|
||||
using UnityEngine.UI;
|
||||
#endif
|
||||
|
||||
namespace MoreMountains.Feel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class used to handle Feel's Snake demo's snake "head", the part controlled by the player
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class Snake : MonoBehaviour
|
||||
{
|
||||
[Header("Movement")]
|
||||
/// the snake's movement speed
|
||||
public float Speed = 5f;
|
||||
/// the speed multiplier to apply at most times
|
||||
public float NormalSpeedMultiplier = 1f;
|
||||
/// the rate at which speed should vary
|
||||
public float SpeedChangeRate = 0.2f;
|
||||
/// the current direction of the snake
|
||||
public Vector3 Direction = Vector2.right;
|
||||
|
||||
[Header("Boost")]
|
||||
/// the speed multiplier to apply to the speed when boosting
|
||||
public float BoostMultiplier = 2f;
|
||||
/// the duration of the boost, in seconds
|
||||
public float BoostDuration = 2f;
|
||||
|
||||
[Header("BodyParts")]
|
||||
/// the prefab to use for body parts
|
||||
public SnakeBodyPart BodyPartPrefab;
|
||||
/// the offset to apply between two parts
|
||||
public int BodyPartsOffset = 7;
|
||||
/// the maximum amount of body parts for this snake
|
||||
public int MaxAmountOfBodyParts = 10;
|
||||
/// the minimum duration, in seconds, between 2 allowed parts losses
|
||||
public float MinTimeBetweenLostParts = 2f;
|
||||
|
||||
[Header("Bindings")]
|
||||
#if MM_UI
|
||||
/// a Text component on which to display our current score
|
||||
public Text PointsCounter;
|
||||
#endif
|
||||
|
||||
[Header("Feedbacks")]
|
||||
/// a feedback to play when the snake turns
|
||||
public MMFeedbacks TurnFeedback;
|
||||
/// a feedback to play when the snake teleports to the other side of the screen
|
||||
public MMFeedbacks TeleportFeedback;
|
||||
/// a feedback to play when teleporting once
|
||||
public MMFeedbacks TeleportOnceFeedback;
|
||||
/// a feedback to play when eating snake food
|
||||
public MMFeedbacks EatFeedback;
|
||||
/// a feedback to play when losing a body part
|
||||
public MMFeedbacks LoseFeedback;
|
||||
|
||||
[Header("Debug")]
|
||||
[MMReadOnly]
|
||||
public int SnakePoints = 0;
|
||||
[MMReadOnly]
|
||||
public float _speed;
|
||||
[MMReadOnly]
|
||||
public float _speedMultiplier;
|
||||
[MMReadOnly]
|
||||
public float _lastFoodEatenAt = -100f;
|
||||
|
||||
protected Vector3 _newPosition;
|
||||
protected MMPositionRecorder _recorder;
|
||||
public List<SnakeBodyPart> _snakeBodyParts;
|
||||
protected float _lastLostPart = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// On Awake, we initialize our snake's points, speed, position recorder, and body parts container
|
||||
/// </summary>
|
||||
protected void Awake()
|
||||
{
|
||||
_speed = Speed;
|
||||
SnakePoints = 0;
|
||||
_recorder = this.gameObject.GetComponent<MMPositionRecorder>();
|
||||
#if MM_UI
|
||||
PointsCounter.text = "0";
|
||||
#endif
|
||||
_snakeBodyParts = new List<SnakeBodyPart>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every frame, we check for input and move our snake's head
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
HandleInput();
|
||||
HandleMovement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every frame, looks for turn input
|
||||
/// </summary>
|
||||
protected virtual void HandleInput()
|
||||
{
|
||||
if (FeelDemosInputHelper.CheckMainActionInputPressedThisFrame())
|
||||
{
|
||||
Turn();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every frame, moves the snake's head's position
|
||||
/// </summary>
|
||||
protected virtual void HandleMovement()
|
||||
{
|
||||
_speedMultiplier = (Time.time - _lastFoodEatenAt < BoostDuration) ? BoostMultiplier : NormalSpeedMultiplier;
|
||||
_speed = MMMaths.Lerp(_speed, Speed * _speedMultiplier, SpeedChangeRate, Time.deltaTime);
|
||||
_newPosition = (_speed * Time.deltaTime * Direction);
|
||||
this.transform.position += _newPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when turning, rotates the snake's head, changes its direction, plays a feedback
|
||||
/// </summary>
|
||||
public virtual void Turn()
|
||||
{
|
||||
TurnFeedback?.PlayFeedbacks();
|
||||
Direction = MMMaths.RotateVector2(Direction, 90f);
|
||||
this.transform.Rotate(new Vector3(0f,0f,90f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the snake head's MMViewportEdgeTeleporter, defines what happens when the snake is teleported to the other side of the screen
|
||||
/// </summary>
|
||||
public virtual void Teleport()
|
||||
{
|
||||
StartCoroutine(TeleportCo());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A coroutine used to teleport the snake to the other side of the screen
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator TeleportCo()
|
||||
{
|
||||
TeleportFeedback?.PlayFeedbacks();
|
||||
|
||||
TeleportOnceFeedback?.PlayFeedbacks();
|
||||
|
||||
yield return MMCoroutine.WaitForFrames(BodyPartsOffset);
|
||||
|
||||
int total = _snakeBodyParts.Count;
|
||||
float feedbacksIntensity = 0f;
|
||||
float part = 1 / (float)total;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
yield return MMCoroutine.WaitForFrames(BodyPartsOffset/2);
|
||||
feedbacksIntensity = 1 - i * part;
|
||||
TeleportFeedback?.PlayFeedbacks(this.transform.position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when eating food, triggers visual effects, increases points, plays a feedback
|
||||
/// </summary>
|
||||
public virtual void Eat()
|
||||
{
|
||||
EatEffect();
|
||||
|
||||
EatFeedback?.PlayFeedbacks();
|
||||
SnakePoints++;
|
||||
#if MM_UI
|
||||
PointsCounter.text = SnakePoints.ToString();
|
||||
#endif
|
||||
StartCoroutine(EatCo());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A coroutine used to go through every snake body part and trigger its Eat MMFeedbacks
|
||||
/// </summary>
|
||||
protected virtual IEnumerator EatCo()
|
||||
{
|
||||
int total = _snakeBodyParts.Count;
|
||||
float feedbacksIntensity = 0f;
|
||||
float part = 1 / (float)total;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
if (i >= _snakeBodyParts.Count)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
yield return MMCoroutine.WaitForFrames(BodyPartsOffset/2);
|
||||
feedbacksIntensity = 1 - i * part;
|
||||
if (i == total - 1)
|
||||
{
|
||||
if ((i < MaxAmountOfBodyParts - 1) && (_snakeBodyParts.Count > i))
|
||||
{
|
||||
if (_snakeBodyParts[i] != null)
|
||||
{
|
||||
_snakeBodyParts[i].New();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_snakeBodyParts[i].Eat(feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When eating food, we instantiate a new body part unless we've reached our limit
|
||||
/// </summary>
|
||||
public virtual void EatEffect()
|
||||
{
|
||||
_lastFoodEatenAt = Time.time;
|
||||
if (SnakePoints < MaxAmountOfBodyParts - 1)
|
||||
{
|
||||
SnakeBodyPart part = Instantiate(BodyPartPrefab);
|
||||
SceneManager.MoveGameObjectToScene(part.gameObject, this.gameObject.scene);
|
||||
part.transform.position = this.transform.position;
|
||||
part.TargetRecorder = _recorder;
|
||||
part.Offset = ((SnakePoints) * BodyPartsOffset) + BodyPartsOffset + 1;
|
||||
part.Index = _snakeBodyParts.Count;
|
||||
part.name = "SnakeBodyPart_" + part.Index;
|
||||
_snakeBodyParts.Add(part);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When we lose a body part, we play a feedback, destroy the last part, lose points, and update our points display
|
||||
/// </summary>
|
||||
/// <param name="part"></param>
|
||||
public virtual void Lose(SnakeBodyPart part)
|
||||
{
|
||||
if (Time.time - _lastLostPart < MinTimeBetweenLostParts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastLostPart = Time.time;
|
||||
LoseFeedback?.PlayFeedbacks(part.transform.position);
|
||||
Destroy(_snakeBodyParts[_snakeBodyParts.Count-1].gameObject);
|
||||
_snakeBodyParts.RemoveAt(_snakeBodyParts.Count-1);
|
||||
SnakePoints--;
|
||||
#if MM_UI
|
||||
PointsCounter.text = SnakePoints.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Feel/FeelDemos/Snake/Scripts/Snake.cs.meta
Normal file
11
Assets/Feel/FeelDemos/Snake/Scripts/Snake.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f1b94c5b0e53534094b6c13350a5d2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
92
Assets/Feel/FeelDemos/Snake/Scripts/SnakeBodyPart.cs
Normal file
92
Assets/Feel/FeelDemos/Snake/Scripts/SnakeBodyPart.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Feedbacks;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class used to handle Feel's Snake demo's snake bodyparts
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class SnakeBodyPart : MonoBehaviour
|
||||
{
|
||||
/// a position recorder this body part will look at to know where to go to
|
||||
public MMPositionRecorder TargetRecorder;
|
||||
/// a feedback to play when food gets eaten
|
||||
public MMFeedbacks EatFeedback;
|
||||
/// a feedback to play when this part appears
|
||||
public MMFeedbacks NewFeedback;
|
||||
public int Offset = 20;
|
||||
public int Index = 0;
|
||||
|
||||
protected Snake _snake;
|
||||
protected BoxCollider2D _collider2D;
|
||||
|
||||
/// <summary>
|
||||
/// On awake we store our collider and enable it after a delay
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
_collider2D = this.gameObject.MMGetComponentNoAlloc<BoxCollider2D>();
|
||||
StartCoroutine(ActivateCollider());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates this part's collider
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator ActivateCollider()
|
||||
{
|
||||
yield return MMCoroutine.WaitFor(1f);
|
||||
_collider2D.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On update, we move to the recorded position of our predecessor
|
||||
/// </summary>
|
||||
protected void Update()
|
||||
{
|
||||
this.transform.position = TargetRecorder.Positions[Offset];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the snake eats a new food
|
||||
/// </summary>
|
||||
/// <param name="intensity"></param>
|
||||
public virtual void Eat(float intensity)
|
||||
{
|
||||
EatFeedback?.PlayFeedbacks(this.transform.position, intensity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when instantiating a new body part
|
||||
/// </summary>
|
||||
public virtual void New()
|
||||
{
|
||||
NewFeedback?.Initialization();
|
||||
NewFeedback?.PlayFeedbacks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If we connect with the snake's head, we lose a part
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
protected void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
if (Index == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_snake = other.GetComponent<Snake>();
|
||||
|
||||
if (_snake != null)
|
||||
{
|
||||
_snake.Lose(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeBodyPart.cs.meta
Normal file
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeBodyPart.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b83ebdb48c79df04381830c0d5dc54a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
58
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFood.cs
Normal file
58
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFood.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Feedbacks;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feel
|
||||
{
|
||||
/// <summary>
|
||||
/// This class handles Feel's Snake demo's food objects, that the snake has to eat to score points
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class SnakeFood : MonoBehaviour
|
||||
{
|
||||
/// a duration (in seconds) during which the food is inactive before moving it to another position
|
||||
public float OffDelay = 1f;
|
||||
/// the food's visual representation
|
||||
public GameObject Model;
|
||||
/// a feedback to play when food gets eaten
|
||||
public MMFeedbacks EatFeedback;
|
||||
/// a feedback to play when food appears
|
||||
public MMFeedbacks AppearFeedback;
|
||||
/// the food spawner
|
||||
public SnakeFoodSpawner Spawner { get; set; }
|
||||
|
||||
protected Snake _snake;
|
||||
|
||||
/// <summary>
|
||||
/// When this food gets eaten, we play its eat feedback, and start moving it somewhere else in the scene
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
protected void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
_snake = other.GetComponent<Snake>();
|
||||
|
||||
if (_snake != null)
|
||||
{
|
||||
_snake.Eat();
|
||||
EatFeedback?.PlayFeedbacks();
|
||||
StartCoroutine(MoveFood());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the food to another spot
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator MoveFood()
|
||||
{
|
||||
Model.SetActive(false);
|
||||
yield return MMCoroutine.WaitFor(OffDelay);
|
||||
Model.SetActive(true);
|
||||
this.transform.position = Spawner.DetermineSpawnPosition();
|
||||
AppearFeedback?.PlayFeedbacks();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFood.cs.meta
Normal file
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFood.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22853891040a0ec409c2ba44a96285d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
56
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFoodSpawner.cs
Normal file
56
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFoodSpawner.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace MoreMountains.Feel
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple class used to spawn snake food in Feel's Snake demo scene
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class SnakeFoodSpawner : MonoBehaviour
|
||||
{
|
||||
/// the food prefab to spawn
|
||||
public SnakeFood SnakeFoodPrefab;
|
||||
/// the maximum amount of food in the scene
|
||||
public int AmountOfFood = 3;
|
||||
/// the minimum coordinates to spawn at (in viewport units)
|
||||
public Vector2 MinRandom = new Vector2(0.1f, 0.1f);
|
||||
/// the maximum coordinates to spawn at (in viewport units)
|
||||
public Vector2 MaxRandom = new Vector2(0.9f, 0.9f);
|
||||
|
||||
protected List<SnakeFood> Foods;
|
||||
protected Camera _mainCamera;
|
||||
|
||||
/// <summary>
|
||||
/// On start, instantiates food
|
||||
/// </summary>
|
||||
protected virtual void Start()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
Foods = new List<SnakeFood>();
|
||||
for (int i = 0; i < AmountOfFood; i++)
|
||||
{
|
||||
SnakeFood food = Instantiate(SnakeFoodPrefab);
|
||||
SceneManager.MoveGameObjectToScene(food.gameObject, this.gameObject.scene);
|
||||
food.transform.position = DetermineSpawnPosition();
|
||||
food.Spawner = this;
|
||||
Foods.Add(food);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a valid position at which to spawn food
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual Vector3 DetermineSpawnPosition()
|
||||
{
|
||||
Vector3 newPosition = MMMaths.RandomVector2(MinRandom, MaxRandom);
|
||||
newPosition.z = 10f;
|
||||
newPosition = _mainCamera.ViewportToWorldPoint(newPosition);
|
||||
return newPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFoodSpawner.cs.meta
Normal file
11
Assets/Feel/FeelDemos/Snake/Scripts/SnakeFoodSpawner.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0eb40a74b537b2469752ad2107a03fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user