public GameObject[] ObjectsForDestroy;
private void CreateDirectory()
{
if (Directory.Exists(_directoryPath) == false)
{
Directory.CreateDirectory(_directoryPath);
}
}
private void CreateFile()
{
if (File.Exists(_path)) return;
var json = JsonUtility.ToJson(CompleteStatus.Locked);
using (var writer = File.CreateText(_path))
{
writer.Write(json);
writer.Close();
}
}
namespace Levels
{
public interface ILevel : IVisualization<ILevelView>
{
void Load();
void Complete(CompleteStatus status);
}
}
namespace Levels
{
public interface IVisualization<in TView>
{
void Visualize(TView view);
}
}
namespace Levels
{
public enum CompleteStatus
{
Locked,
Uncompleted,
OneStar,
TwoStars,
ThreeStars
}
public interface ILevelView
{
void DrawCompletion(CompleteStatus status);
}
}
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Levels
{
public abstract class Level : ILevel
{
private readonly string _name;
private readonly string _path;
private readonly string _directoryPath = $"{Application.persistentDataPath}/Saves";
protected Level(string name)
{
_name = name;
_path = $"{_directoryPath}/{_name}.json";
CreateDirectory();
CreateFile();
}
public void Load()
{
SceneManager.LoadScene(_name);
}
public void Complete(CompleteStatus status)
{
SaveStatus(status);
}
public void Visualize(ILevelView view)
{
view.DrawCompletion(LoadStatusFromJson());
}
private void CreateDirectory()
{
if (Directory.Exists(_directoryPath) == false)
{
Directory.CreateDirectory(_directoryPath);
}
}
private void CreateFile()
{
if (File.Exists(_path)) return;
var json = JsonUtility.ToJson(CompleteStatus.Locked);
using (var writer = File.CreateText(_path))
{
writer.Write(json);
writer.Close();
}
}
private void SaveStatus(CompleteStatus status)
{
var json = JsonUtility.ToJson(status);
File.WriteAllText(_path, json);
}
private CompleteStatus LoadStatusFromJson()
{
var file = File.ReadAllText(_path);
return JsonUtility.FromJson<CompleteStatus>(file);
}
}
}
namespace Levels
{
public class SecondLevel : Level
{
public SecondLevel() : base(nameof(SecondLevel))
{
}
}
}
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Levels
{
public class LevelView : MonoBehaviour, ILevelView
{
[SerializeField] private Image _firstStar, _secondStar, _thirdStar;
[SerializeField] private Button _button;
[SerializeField] private Color _inactiveStarColor, _activeStarColor;
public void DrawCompletion(CompleteStatus status)
{
switch (status)
{
case CompleteStatus.Locked:
_firstStar.color = _inactiveStarColor;
_secondStar.color = _inactiveStarColor;
_thirdStar.color = _inactiveStarColor;
_button.interactable = false;
break;
case CompleteStatus.Uncompleted:
_button.interactable = true;
_firstStar.color = _inactiveStarColor;
_secondStar.color = _inactiveStarColor;
_thirdStar.color = _inactiveStarColor;
break;
case CompleteStatus.OneStar:
_button.interactable = true;
_firstStar.color = _activeStarColor;
_secondStar.color = _inactiveStarColor;
_thirdStar.color = _inactiveStarColor;
break;
case CompleteStatus.TwoStars:
_button.interactable = true;
_firstStar.color = _activeStarColor;
_secondStar.color = _activeStarColor;
_thirdStar.color = _inactiveStarColor;
break;
case CompleteStatus.ThreeStars:
_button.interactable = true;
_firstStar.color = _activeStarColor;
_secondStar.color = _activeStarColor;
_thirdStar.color = _activeStarColor;
break;
default: throw new ArgumentException();
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
namespace Levels
{
public class LevelList : MonoBehaviour
{
[SerializeField] private LevelView _levelViewPrefab;
[SerializeField] private Transform _levelsParent;
private ILevel[] _levels;
private void Awake()
{
var first = new FirstLevel();
var second = new SecondLevel();
var third = new ThirdLevel();
var fourth = new FourthLevel();
var fifth = new FifthLevel();
_levels = new ILevel[]
{
first, second, third, fourth, fifth
};
//test
first.Complete(CompleteStatus.OneStar);
second.Complete(CompleteStatus.TwoStars);
third.Complete(CompleteStatus.ThreeStars);
fourth.Complete(CompleteStatus.Uncompleted);
fifth.Complete(CompleteStatus.Locked);
VisualizeLevels(_levels);
}
private void VisualizeLevels(IEnumerable<ILevel> levels)
{
foreach (var level in levels)
{
var view = Instantiate(_levelViewPrefab, _levelsParent);
level.Visualize(view);
}
}
}
}
Hsys
PCont
TakeDamage
DamageCondition
namespace Coroutines
{
public interface IHealth
{
bool IsOver { get; }
void Lose(float amount);
void Restore(float amount);
}
}
using System;
using UnityEngine;
namespace Coroutines
{
public class Health : IHealth
{
private readonly float _max;
private readonly float _min;
private float _current;
public Health(float max, float min = 0)
{
_max = max;
_min = min;
_current = _max;
}
public bool IsOver => _current <= _min;
public void Lose(float amount)
{
if (amount <= 0) throw new ArgumentException();
SetCurrent(_current - amount);
}
public void Restore(float amount)
{
if (amount <= 0) throw new ArgumentException();
SetCurrent(_current + amount);
}
private void SetCurrent(float amount)
{
_current = Mathf.Clamp(amount, _min, _max);
}
}
}
namespace Coroutines
{
public interface IDamageable
{
bool IsDead { get; }
void ApplyDamage(float amount);
}
}
using UnityEngine;
namespace Coroutines
{
public class Character : MonoBehaviour, IDamageable
{
[SerializeField] private float _maxHp = 100f;
private IHealth _health;
public bool IsDead => _health.IsOver;
private void Awake()
{
_health = new Health(_maxHp);
}
public void ApplyDamage(float amount) => _health.Lose(amount);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Coroutines
{
public class Trap : MonoBehaviour
{
[SerializeField] private float _damage = 10f;
[SerializeField] private float _delay = 2f;
private readonly Dictionary<IDamageable, Coroutine> _targets = new();
private void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent(out IDamageable damageable))
{
var coroutine = StartCoroutine(Damaging(damageable));
_targets.Add(damageable, coroutine);
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.TryGetComponent(out IDamageable damageable))
{
var coroutine = _targets[damageable];
StopCoroutine(coroutine);
_targets.Remove(damageable);
}
}
private IEnumerator Damaging(IDamageable target)
{
while (target.IsDead == false)
{
target.ApplyDamage(_damage);
yield return new WaitForSeconds(_delay);
}
}
}
}
using UnityEngine;
namespace BehaviourTree
{
public interface IMovementInput
{
Vector3 Direction { get; }
}
}
using UnityEngine;
namespace BehaviourTree
{
[RequireComponent(typeof(Rigidbody))]
public class Bot : MonoBehaviour
{
[SerializeField] private MonoBehaviour _botInput = null;
[SerializeField] private float _speed = 10f;
private Rigidbody _rb;
private IMovementInput BotInput => (IMovementInput) _botInput;
private void OnValidate()
{
if (_botInput is IMovementInput) return;
Debug.LogError($"{nameof(_botInput)} should implement {nameof(IMovementInput)}");
_botInput = null;
}
private void Awake()
{
_rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
var velocity = new Vector3(BotInput.Direction.x, 0, BotInput.Direction.z) * _speed;
_rb.velocity = velocity;
}
public void LookAt(Vector3 direction)
{
var rotation = Quaternion.LookRotation(direction);
transform.rotation = rotation;
}
}
}
using UnityEngine;
namespace BehaviourTree
{
public class BotInput : MonoBehaviour, IMovementInput
{
public Vector3 Direction { get; set; }
}
}
using UnityEngine;
namespace BehaviourTree
{
public interface ITarget
{
Vector3 Position { get; }
}
}
using UnityEngine;
namespace BehaviourTree
{
public class SomeTarget : MonoBehaviour, ITarget
{
public Vector3 Position => transform.position;
}
}
using BehaviorDesigner.Runtime;
namespace BehaviourTree.Brain
{
public class SharedBotInput : SharedVariable<BotInput>
{
public static implicit operator SharedBotInput(BotInput input) => new SharedBotInput {Value = input};
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using UnityEngine;
namespace BehaviourTree.Brain
{
public class SetRandomDirection : Action
{
public SharedVector3 Direction;
public override TaskStatus OnUpdate()
{
Direction.Value = Vector3.Scale(Random.insideUnitSphere.normalized, new Vector3(1, 0, 1));
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
namespace BehaviourTree.Brain
{
public class SetInput : Action
{
public SharedBotInput SharedInput;
public SharedVector3 Input;
public override TaskStatus OnUpdate()
{
SharedInput.Value.Direction = Input.Value;
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime.Tasks;
namespace BehaviourTree.Brain
{
public class LookAtDirection : Action
{
public SharedBotInput Direction;
public Bot Bot;
public override TaskStatus OnUpdate()
{
Bot.LookAt(Direction.Value.Direction);
return TaskStatus.Success;
}
}
}
using BehaviorDesigner.Runtime;
namespace BehaviourTree.Brain
{
public class SharedTarget : SharedVariable<SomeTarget>
{
public static implicit operator SharedTarget(SomeTarget target) => new SharedTarget {Value = target};
}
}
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using UnityEngine;
namespace BehaviourTree.Brain
{
public class SetDirectionToTarget : Action
{
public SomeTarget Target;
public Bot Origin;
public SharedVector3 Direction;
public override TaskStatus OnUpdate()
{
var direction = (Target.Position - Origin.transform.position).normalized;
Direction.Value = new Vector3(direction.x, 0, direction.z);
return TaskStatus.Success;
}
}
}
namespace BehaviourTree.Brain
{
public interface IEye
{
bool InSight(ITarget target);
}
}
using UnityEngine;
namespace BehaviourTree.Brain
{
public class Eye : IEye
{
private readonly float _fov;
private readonly Transform _origin;
public Eye(float fov, Transform origin)
{
_fov = fov;
_origin = origin;
}
public bool InSight(ITarget target)
{
var direction = target.Position - _origin.position;
var angle = Mathf.Acos(Vector3.Dot(_origin.forward.normalized, direction.normalized)) * Mathf.Rad2Deg;
return angle <= _fov;
}
}
}
Что я не так делаю с интерфейсами?
namespace Health
{
public interface IHealth
{
void Lose(int amount);
void Restore(int amount);
}
public interface IMutable<out T>
{
T Current { get; }
}
public interface IFinal
{
event Action Over;
}
}
using System;
using UnityEngine;
namespace Health
{
public class Health : IHealth, IFinal, IMutable<int>
{
public event Action Over;
private readonly int _max;
private const int Min = 0;
public Health(int max)
{
_max = max;
Current = _max;
}
public int Current { get; private set; }
public void Lose(int amount)
{
SetCurrent(Current - amount);
}
public void Restore(int amount)
{
SetCurrent(Current + amount);
}
private void SetCurrent(int amount)
{
Current = Mathf.Clamp(amount, Min, _max);
if (Current == Min) Over?.Invoke();
}
}
}
namespace Health
{
public interface IDamageable
{
void ApplyDamage(int amount);
}
}
using UnityEngine;
namespace Health
{
public class Knight : MonoBehaviour, IDamageable
{
[SerializeField] private int _maxHealth = 100;
private Health _health;
private void Awake()
{
_health = new Health(_maxHealth);
}
private void OnEnable()
{
_health.Over += Die;
}
private void OnDisable()
{
_health.Over -= Die;
}
public void ApplyDamage(int amount)
{
_health.Lose(amount);
Debug.Log($"Damaged, hp left - {_health.Current}");
}
private void Die()
{
Debug.Log("Died");
Destroy(gameObject);
}
}
}
using UnityEngine;
namespace Health
{
public class Enemy : MonoBehaviour
{
[SerializeField] private int _damage = 50;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent(out IDamageable damageable))
{
damageable.ApplyDamage(_damage);
}
}
}
}
namespace SmoothMovement
{
public interface IPlayerInput
{
float Acceleration { get; }
}
}
namespace SmoothMovement
{
public interface ISmoothAcceleration
{
float Smooth(float acceleration, float input);
}
}
using UnityEngine;
namespace SmoothMovement
{
public class MobileInput : MonoBehaviour, IPlayerInput
{
public float Acceleration { get; private set; }
[SerializeField] private float _minAcceleration = -1f;
[SerializeField] private float _maxAcceleration = 1f;
[SerializeField] private float _smoothMultiplier = 5f;
[Range(0, 1)] [SerializeField] private float _fadingSpeed = 0.01f;
private ISmoothAcceleration _smoothing;
private void Awake()
{
_smoothing = new SmoothedAcceleration(_minAcceleration, _maxAcceleration, _smoothMultiplier, _fadingSpeed);
}
private void Update()
{
Acceleration = _smoothing.Smooth(Acceleration, Input.acceleration.x);
}
}
}
using UnityEngine;
namespace SmoothMovement
{
public class SmoothedAcceleration : ISmoothAcceleration
{
private readonly float _multiplier;
private readonly float _minValue;
private readonly float _maxValue;
private readonly float _fadingSpeed;
public SmoothedAcceleration(float minValue, float maxValue, float multiplier, float fadeSpeed)
{
_minValue = minValue;
_maxValue = maxValue;
_multiplier = multiplier;
_fadingSpeed = fadeSpeed;
}
public float Smooth(float acceleration, float input)
{
if (input == 0)
{
acceleration = Mathf.Lerp(acceleration, 0, _fadingSpeed);
return acceleration;
}
acceleration += input * _multiplier * Time.deltaTime;
acceleration = Mathf.Clamp(acceleration, _minValue, _maxValue);
return acceleration;
}
}
}
using UnityEngine;
namespace SmoothMovement
{
public class TestMovement : MonoBehaviour
{
[SerializeField] private MonoBehaviour _input = null;
[SerializeField] private float _speed = 10f;
private IPlayerInput Input => (IPlayerInput)_input;
private void OnValidate()
{
if (_input is IPlayerInput) return;
Debug.LogError($"{nameof(_input)} should implement {nameof(IPlayerInput)}");
_input = null;
}
private void FixedUpdate()
{
Move(Input.Acceleration);
}
private void Move(float direction)
{
if (direction == 0) return;
var position = transform.position;
position.x += direction * _speed * Time.deltaTime;
transform.position = position;
}
}
}
public class KeyboardInput : MonoBehaviour, IPlayerInput
{
public event Action ActionButtonPressed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
ActionButtonPressed?.Invoke();
}
}
}
public interface IPlayerInput
{
public event Action ActionButtonPressed;
}
[RequireComponent(typeof(Collider2D))]
public class DialogueTrigger : MonoBehaviour
{
[SerializeField] private KeyboardInput _input;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.TryGetComponent(out Player player))
{
_input.ActionButtonPressed += StartDialogue;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.TryGetComponent(out Player player))
{
_input.ActionButtonPressed -= StartDialogue;
EndDialogue();
}
}
private void StartDialogue()
{
Debug.Log("DialogueStarted");
}
private void EndDialogue()
{
Debug.Log("DialogueEnded");
}
}
var distance = (point.position - transform.position).magnitude;
public class Example : MonoBehaviour
{
[SerializeField] private Transform _pointA, _pointB = null;
[SerializeField] private float _speed = 10f;
[SerializeField] private float _closeDistance = 0.2f;
private Transform _currentPoint;
private void Start()
{
_currentPoint = _pointA;
}
private void FixedUpdate()
{
MoveTo(_currentPoint.position);
}
private void MoveTo(Vector3 position)
{
var nextPosition = transform.position;
var delta = (position - transform.position).normalized;
delta *= _speed * Time.deltaTime;
nextPosition += delta;
transform.position = nextPosition;
UpdatePoint();
}
private void UpdatePoint()
{
var distance = (_currentPoint.position - transform.position).magnitude;
if (distance <= _closeDistance)
{
SwitchPoint();
}
}
private void SwitchPoint()
{
_currentPoint = _currentPoint == _pointA ? _pointB : _pointA;
}
}
[SerializeField] private Slider _slider;
private void OnEnable()
{
_slider.onValueChanged.AddListener(ChangeVolume);
}
private void OnDisable()
{
_slider.onValueChanged.RemoveListener(ChangeVolume);
}
private void ChangeVolume(float amount)
{
//изменить громкость на значение amount
}