613 lines
27 KiB
C#
613 lines
27 KiB
C#
using UnityEngine;
|
||
using RR.Common.Interfaces;
|
||
using RR.Game2D.Core.Managers;
|
||
using RR.Game2D.Behaviour;
|
||
using RR.Game2D.Core.Managers.Interfaces;
|
||
|
||
namespace RR.Game2D.Entities.Projectiles
|
||
{
|
||
/// <summary>
|
||
/// Подбираемый снаряд с возможностью отскока, возврата к владельцу и взаимодействия с игровыми сущностями.
|
||
/// </summary>
|
||
[DisallowMultipleComponent]
|
||
[RequireComponent(typeof(Rigidbody2D),
|
||
typeof(CapsuleCollider2D))]
|
||
public class PickableProjectile : PhysicsProjectile, IPickable, IBouncy, IRecallable
|
||
{
|
||
public static PickableProjectile Instance { get; private set; }
|
||
|
||
public enum State { Held, Airborne, Grounded, Returning }
|
||
public State CurrentState { get; private set; } = State.Grounded;
|
||
|
||
[Header("Подбор и прикрепление")]
|
||
[SerializeField] private Vector3 heldLocalOffset = new(0.25f, 0.1f, 0f);
|
||
[SerializeField] private float heldScale = 1.5f;
|
||
[SerializeField] private float ignoreOwnerTimeOnThrow = 0.1f;
|
||
|
||
[Header("Приземление")]
|
||
[SerializeField] private float groundVelocity = 1f;
|
||
[SerializeField] private float maxGroundSlideTime = 1f;
|
||
[SerializeField] private bool lockWhenLanded = true;
|
||
|
||
[Header("Отскоки")]
|
||
[SerializeField] private int maxBounces = 3;
|
||
[SerializeField] private float minBounceSpeed = 0.8f;
|
||
[SerializeField] private float stickDelayAfterImpact = 0.03f;
|
||
|
||
[Header("Возврат")]
|
||
[SerializeField, Range(0, 100)] int recallScreenMarginPercent = 5;
|
||
[SerializeField] private float recallArriveDistance = 1f;
|
||
[SerializeField] private float recallSpeed = 16f;
|
||
[SerializeField] private float recallStartDistance = 8f;
|
||
|
||
[Header("Gizmos")]
|
||
[SerializeField] private bool drawGizmos;
|
||
[SerializeField] private Color gizmoColor = new (0.2f, 0.8f, 1f, 0.35f);
|
||
|
||
private Transform holderAttachPoint;
|
||
private int bounceCounter;
|
||
private float lastImpactTime = -999f;
|
||
private readonly Vector3 baseLocalScale = Vector3.one;
|
||
|
||
private bool hadBeenHeld;
|
||
private GameObject lastKnownHolder;
|
||
private float lastGroundTouchTime;
|
||
|
||
private GameObject jumpPad;
|
||
|
||
// -------------------------------------------
|
||
// Методы Unity
|
||
//
|
||
|
||
#region Unity Methods
|
||
|
||
//---------------------------
|
||
// Singleton
|
||
//---------------------------
|
||
/// <summary>
|
||
/// Инициализация компонентов и создание вспомогательной прыжковой платформы (jumpPad).
|
||
/// Реализуется как Singleton: если экземпляр уже существует — уничтожаем дубликат.
|
||
/// </summary>
|
||
protected override void Awake()
|
||
{
|
||
// Получение ссылок на компоненты Rigidbody2D и Collider
|
||
_rb = GetComponent<Rigidbody2D>();
|
||
|
||
_col = GetComponent<CapsuleCollider2D>();
|
||
// Пока в Awake оставляем коллайдер в режиме триггера — это переставим в Start/другие состояния по необходимости
|
||
_col.isTrigger = true;
|
||
|
||
// Простая реализация singleton-а — предотвращаем дублирование снаряда, если это ожидаемое поведение проекта
|
||
if (Instance != null && Instance != this)
|
||
{
|
||
Destroy(gameObject);
|
||
return;
|
||
}
|
||
|
||
Instance = this;
|
||
|
||
// Создаём вспомогательный объект "jumpPad" программно. Он используется для реакции игрока на то, что снаряд лежит на земле.
|
||
// Создание через код даёт гибкость при отсутствии заранее подготовленного префаба.
|
||
jumpPad = new GameObject("Projectile Jump Pad");
|
||
jumpPad.AddComponent<BoxCollider2D>();
|
||
jumpPad.AddComponent<JumpPad2D>();
|
||
jumpPad.GetComponent<BoxCollider2D>().isTrigger = true;
|
||
Vector3 jumpPadScale = jumpPad.transform.localScale;
|
||
// Уменьшаем по высоте — платформа тонкая
|
||
jumpPadScale.y = 0.1f;
|
||
jumpPadScale.x = 2f;
|
||
jumpPad.transform.localScale = jumpPadScale;
|
||
jumpPad.SetActive(false);
|
||
// Подписываемся на событие прыжка с платформы
|
||
jumpPad.GetComponent<JumpPad2D>().PlayerJumped += OnPlayerJumped;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очистка при уничтожении — отписка и уничтожение вспомогательного объекта.
|
||
/// </summary>
|
||
private void OnDestroy()
|
||
{
|
||
if (Instance == this) Instance = null;
|
||
Destroy(jumpPad);
|
||
}
|
||
//---------------------------
|
||
//
|
||
//---------------------------
|
||
|
||
/// <summary>
|
||
/// Устанавливаем параметры физики и первоначальное состояние снаряда.
|
||
/// </summary>
|
||
private void Start()
|
||
{
|
||
_rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
|
||
_col.isTrigger = false;
|
||
|
||
if (CurrentState != State.Held)
|
||
BecomeGrounded();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Фиксированный апдейт: обработка смены состояний, поворот в направлении полёта и инициирование возврата.
|
||
/// </summary>
|
||
protected override void FixedUpdate()
|
||
{
|
||
base.FixedUpdate();
|
||
|
||
// Если не нужно блокировать при приземлении — проверяем скорость и переводим в состояние 'Grounded'
|
||
if (!lockWhenLanded && CurrentState == State.Airborne)
|
||
{
|
||
if (_rb.linearVelocity.sqrMagnitude < (groundVelocity * groundVelocity))
|
||
BecomeGrounded();
|
||
}
|
||
|
||
// В полёте и при возврате снаряд ориентируется по вектору скорости
|
||
if ((CurrentState == State.Airborne || CurrentState == State.Returning) && _rb.linearVelocity.sqrMagnitude > 0.0001f)
|
||
transform.up = _rb.linearVelocity.normalized;
|
||
|
||
// Дополнительная страховка перехода в grounded по скорости
|
||
if (CurrentState == State.Airborne && _rb.linearVelocity.magnitude < groundVelocity)
|
||
BecomeGrounded();
|
||
|
||
// Если снаряд когда-то был в руках — он может начать возвращаться автоматически
|
||
if (hadBeenHeld && CurrentState != State.Held)
|
||
Recall();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обработка столкновения. Пытаемся подобрать при касании игрока, наносим урон врагам, обрабатываем отскоки и приземление.
|
||
/// </summary>
|
||
/// <param name="collision">Данные столкновения</param>
|
||
protected override void OnCollisionEnter2D(Collision2D collision)
|
||
{
|
||
// Нахождение игрового объекта, с которым мы столкнулись. Учтём разные варианты: rigidbody другого коллайдера или сам объект.
|
||
var otherRb = collision.rigidbody ? collision.rigidbody : collision.otherRigidbody;
|
||
var otherGO = (collision.collider ? collision.collider.attachedRigidbody?.gameObject : null)
|
||
?? otherRb?.gameObject
|
||
?? collision.gameObject;
|
||
if (otherGO != null)
|
||
{
|
||
if (otherGO.CompareTag("Player") && CurrentState != State.Held)
|
||
{
|
||
// Попытка подобрать снаряд игроком
|
||
if (TryPickup(otherGO)) return;
|
||
}
|
||
else if (otherGO.CompareTag("Enemy"))
|
||
{
|
||
// Наносим урон врагу через его контроллер, если он есть
|
||
EnemyController enemyController = otherGO.GetComponent<EnemyController>();
|
||
if (enemyController != null)
|
||
{
|
||
enemyController.TakeDamage(projectileDamage);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Если слой не предназначен для нанесения урона/эффектов — выходим
|
||
if (!IsInHitLayer(collision.collider.gameObject.layer)) return;
|
||
|
||
// Точка контакта для эффектов
|
||
Vector2 point = collision.GetContact(0).point;
|
||
OnHit(collision, point, collision.relativeVelocity);
|
||
|
||
// Логика отскока и приземления
|
||
if (CurrentState == State.Airborne)
|
||
{
|
||
if (TryBounce(collision))
|
||
{
|
||
lastImpactTime = Time.time;
|
||
return;
|
||
}
|
||
|
||
BecomeGrounded();
|
||
}
|
||
|
||
if (collision.collider.CompareTag("Ground"))
|
||
lastGroundTouchTime = Time.time;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Контролирует длительность скольжения по земле — если прошло слишком много времени, переводим в grounded.
|
||
/// </summary>
|
||
/// <param name="collision">Данные столкновения</param>
|
||
private void OnCollisionStay2D(Collision2D collision)
|
||
{
|
||
if (collision.collider.CompareTag("Ground"))
|
||
{
|
||
if (Time.time - lastGroundTouchTime > maxGroundSlideTime && CurrentState != State.Returning)
|
||
BecomeGrounded();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Обработка триггерного входа: подбор игроком, нанесение урона врагам или делегирование базовому методу.
|
||
/// </summary>
|
||
/// <param name="other">Коллайдер, вошедший в триггер</param>
|
||
protected override void OnTriggerEnter2D(Collider2D other)
|
||
{
|
||
if (other.CompareTag("Player"))
|
||
{
|
||
if (TryPickup(other.gameObject)) return;
|
||
}
|
||
else if (other.CompareTag("Enemy"))
|
||
{
|
||
other.TryGetComponent<EnemyController>(out var controller);
|
||
controller.TakeDamage(projectileDamage);
|
||
}
|
||
else
|
||
{
|
||
base.OnTriggerEnter2D(other);
|
||
}
|
||
}
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
#endregion
|
||
|
||
// -------------------------------------------
|
||
// Переопределенные методы родителей
|
||
//
|
||
|
||
#region Parent Methods
|
||
|
||
/// <summary>
|
||
/// Обработка попадания при столкновении (вызывается из OnCollisionEnter2D).
|
||
/// По-умолчанию проигрывает эффекты и вызывает финальную обработку удара.
|
||
/// </summary>
|
||
protected override void OnHit(Collision2D collision, Vector2 point, Vector2 relativeVelocity)
|
||
{
|
||
//TryDealDamage(collision.collider, point, relativeVelocity);
|
||
PlayFX(point);
|
||
|
||
OnImpactFinal(collision);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Финальная обработка удара при наличии Collision2D.
|
||
/// Пытаемся отскочить; если отскок невозможен — считаем, что снаряд приземлился.
|
||
/// </summary>
|
||
/// <param name="collision">Данные столкнов<D0BE><D0B2>ния</param>
|
||
protected override void OnImpactFinal(Collision2D collision)
|
||
{
|
||
if (!TryBounce(collision) && CurrentState == State.Airborne)
|
||
BecomeGrounded();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Финальная обработка удара при наличии Collider2D (например при триггерах).
|
||
/// Если прошло достаточно времени с последнего удара — приземляемся.
|
||
/// </summary>
|
||
/// <param name="other">Коллайдер</param>
|
||
protected override void OnImpactFinal(Collider2D other)
|
||
{
|
||
if (Time.time - lastImpactTime > stickDelayAfterImpact && CurrentState == State.Airborne)
|
||
BecomeGrounded();
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
// -------------------------------------------
|
||
// Подбор
|
||
//
|
||
#region Pickup
|
||
|
||
/// <summary>
|
||
/// Попытка подобрать снаряд указанным владельцем.
|
||
/// Если снаряд уже держат — возвращает true.
|
||
/// </summary>
|
||
/// <param name="newOwner">Игровой объект, пытающийся подобрать снаряд</param>
|
||
/// <param name="optionalAttachPoint">Опциональная точка привязки</param>
|
||
/// <returns>True — если успешно подобран или уже в руках, иначе false</returns>
|
||
public bool TryPickup(GameObject newOwner, Transform optionalAttachPoint = null)
|
||
{
|
||
if (CurrentState == State.Held) return true;
|
||
|
||
owner = newOwner;
|
||
lastKnownHolder = newOwner;
|
||
|
||
holderAttachPoint = optionalAttachPoint ?? FindAttachPoint(newOwner);
|
||
var fireScript = newOwner.GetComponent<PlayerFireScript>();
|
||
if (!holderAttachPoint && fireScript != null)
|
||
holderAttachPoint = fireScript.attachPoint;
|
||
if (!holderAttachPoint)
|
||
holderAttachPoint = newOwner.transform;
|
||
|
||
// Отключаем физическое поведение и делаем снаряд кинематическим, чтобы он "встал" в руки игрока
|
||
_rb.linearVelocity = Vector2.zero;
|
||
_rb.angularVelocity = 0f;
|
||
_rb.bodyType = RigidbodyType2D.Kinematic;
|
||
_col.enabled = false;
|
||
|
||
transform.SetParent(holderAttachPoint);
|
||
// Устанавливаем позицию и вращение относительно точки привязки.
|
||
transform.SetPositionAndRotation(holderAttachPoint.TransformPoint(heldLocalOffset), new Quaternion(0, 90f, 0, 0));
|
||
transform.localScale = baseLocalScale * heldScale;
|
||
|
||
if (fireScript != null) fireScript.BindProjectile(this);
|
||
|
||
//StartCoroutine(IgnoreOwnerTemporarily(owner, ignoreOwnerTimeOnThrow));
|
||
|
||
CurrentState = State.Held;
|
||
_launched = false;
|
||
hadBeenHeld = true;
|
||
|
||
// При подборе убираем jumpPad, чтобы игрок не случайно "прыгнул" с него
|
||
jumpPad.SetActive(false);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Поиск точки привязки на holder (игроке).
|
||
/// Возвращает preferred если задано, иначе ищет в PlayerFireScript, иначе возвращает трансформ самого объекта.
|
||
/// </summary>
|
||
/// <param name="holder">Игрок / владелец</param>
|
||
/// <param name="preferred">Предпочтительная точка привязки</param>
|
||
/// <returns>Transform точки привязки</returns>
|
||
private Transform FindAttachPoint(GameObject holder, Transform preferred = null)
|
||
{
|
||
if (preferred) return preferred;
|
||
|
||
PlayerFireScript fireScript = holder.GetComponent<PlayerFireScript>();
|
||
if (fireScript && fireScript.attachPoint) return fireScript.attachPoint;
|
||
|
||
return holder.transform;
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
// -------------------------------------------
|
||
// Отскок
|
||
//
|
||
#region Bounce
|
||
|
||
/// <summary>
|
||
/// Попытка отскока при столкновении. Возвращает true, если отскок выполнен.
|
||
/// Здесь используется отражение вектора скорости относительно нормали контакта и коэффициент упругости из материала ко<D0BA><D0BE>лайдера.
|
||
/// </summary>
|
||
/// <param name="collision">Данные столкновения</param>
|
||
/// <returns>True — если был отскок</returns>
|
||
public bool TryBounce(Collision2D collision)
|
||
{
|
||
// Короткий таймаут между прыжками, чтобы избежать слишком частых "залипаний" на отскоках
|
||
if (Time.time - lastImpactTime < stickDelayAfterImpact) return true;
|
||
|
||
if (bounceCounter > maxBounces) return false;
|
||
|
||
Vector2 velocityIn = _rb.linearVelocity;
|
||
float speed = velocityIn.magnitude;
|
||
|
||
if (speed < minBounceSpeed) return false;
|
||
|
||
// Нормаль первого контакта
|
||
Vector2 direction = Vector2.Reflect(velocityIn, collision.contacts[0].normal);
|
||
|
||
// Здесь используется bounciness физического материала коллайдера
|
||
_rb.linearVelocity = _col.sharedMaterial.bounciness * direction;
|
||
|
||
|
||
if (collision.collider.CompareTag("Ground") || collision.collider.CompareTag("Trap"))
|
||
bounceCounter++;
|
||
|
||
return true;
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
|
||
// -------------------------------------------
|
||
// Запуск
|
||
//
|
||
|
||
#region Launch
|
||
|
||
/// <summary>
|
||
/// Запуск снаряда: делаем его динамическим, добавляем импульс, настраиваем параметры состояния.
|
||
/// </summary>
|
||
/// <param name="direction">Направление запуска (в мировых координатах)</param>
|
||
/// <param name="speed">Скорость — если значение меньше либо равно нуля, используется initialSpeed</param>
|
||
/// <param name="thrower">Игрок, который бросает снаряд (владелец)</param>
|
||
public override void Launch(Vector2 direction, float speed = -1, GameObject thrower = null)
|
||
{
|
||
if (CurrentState == State.Held)
|
||
transform.SetParent(null);
|
||
|
||
owner = thrower;
|
||
lastKnownHolder = thrower;
|
||
holderAttachPoint = null;
|
||
|
||
_rb.bodyType = RigidbodyType2D.Dynamic;
|
||
_col.enabled = true;
|
||
_col.isTrigger = false;
|
||
|
||
_launched = true;
|
||
|
||
Vector2 launchDirection = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right;
|
||
float finalSpeed = (speed > 0f) ? speed : initialSpeed;
|
||
|
||
_rb.linearVelocity = Vector2.zero;
|
||
_rb.angularVelocity = 0f;
|
||
_rb.AddForce(finalSpeed * _rb.mass * launchDirection, ForceMode2D.Impulse);
|
||
|
||
// Ориентируем снаряд по направлению скорости
|
||
transform.up = launchDirection;
|
||
transform.localScale = baseLocalScale;
|
||
|
||
if (thrower != null && ignoreOwnerTimeOnThrow > 0f)
|
||
StartCoroutine(IgnoreOwnerTemporarily(thrower, ignoreOwnerTimeOnThrow));
|
||
|
||
CurrentState = State.Airborne;
|
||
bounceCounter = 0;
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
// -------------------------------------------
|
||
// Возвращение
|
||
//
|
||
#region Recall
|
||
|
||
/// <summary>
|
||
/// Логика возврата снаряда к последнему известному владельцу. Если снаряд найден вне экрана — переключается в режим Returning и летит к владельцу.
|
||
/// </summary>
|
||
public void Recall()
|
||
{
|
||
GameObject playerGo = lastKnownHolder;
|
||
|
||
// Если нет последнего известного владельца — пытаемся получить игрока из GameManager или найти по тегу Player
|
||
if (playerGo == null || !playerGo.activeInHierarchy)
|
||
playerGo = (GameManager.Instance && GameManager.Instance.PlayerObj)
|
||
? GameManager.Instance.PlayerObj
|
||
: GameObject.FindWithTag("Player");
|
||
|
||
if (!playerGo) return;
|
||
|
||
var attachPoint = FindAttachPoint(playerGo);
|
||
|
||
bool isVisible;
|
||
var mainCamera = Camera.main;
|
||
float margin = recallScreenMarginPercent / 100f;
|
||
|
||
if (mainCamera)
|
||
{
|
||
// Если камера есть — проверяем, видим ли снаряд в пределах экрана (+margin)
|
||
Vector3 viewPoint = mainCamera.WorldToViewportPoint(transform.position);
|
||
isVisible = viewPoint.x >= -margin && viewPoint.x <= 1f + margin
|
||
&& viewPoint.y >= -margin && viewPoint.y <= 1f + margin;
|
||
}
|
||
else
|
||
{
|
||
// Если камеры нет — проверяем дистанцию до владельца как запасной критерий видимости
|
||
Vector2 toOwnerTest = (Vector2)attachPoint.position - _rb.position;
|
||
isVisible = (toOwnerTest.magnitude <= recallStartDistance);
|
||
}
|
||
|
||
Vector2 toOwner = (Vector2)attachPoint.position - _rb.position;
|
||
float dist = toOwner.magnitude;
|
||
|
||
if (CurrentState != State.Returning)
|
||
{
|
||
if (!isVisible)
|
||
{
|
||
// Переходим в режим возврата: включаем физику и делаем триггер, чтобы не мешать столкновениям
|
||
CurrentState = State.Returning;
|
||
_rb.bodyType = RigidbodyType2D.Dynamic;
|
||
_col.isTrigger = true;
|
||
}
|
||
else
|
||
{
|
||
// Если видим снаряд — не начинаем возврат
|
||
return;
|
||
}
|
||
}
|
||
|
||
Vector2 dir = (dist > 0.001f) ? (toOwner / dist) : _rb.linearVelocity.normalized;
|
||
_rb.linearVelocity = dir * recallSpeed;
|
||
|
||
if (_rb.linearVelocity.sqrMagnitude > 0.01f)
|
||
transform.up = _rb.linearVelocity.normalized;
|
||
|
||
// Если достаточно близко — автоматически подбираем
|
||
if (dist <= recallArriveDistance)
|
||
TryPickup(playerGo);
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
|
||
// -------------------------------------------
|
||
// Собственные незаисимые методы
|
||
//
|
||
#region Self methods
|
||
|
||
/// <summary>
|
||
/// Переводит снаряд в состояние "на земле" (Grounded): блокирует или оставляет динамическим,
|
||
/// включает/выключает триггер и активирует jumpPad — платформу, с которой игрок может прыгнуть.
|
||
/// </summary>
|
||
private void BecomeGrounded()
|
||
{
|
||
_launched = false;
|
||
CurrentState = State.Grounded;
|
||
|
||
if (lockWhenLanded)
|
||
{
|
||
// Полностью останавливаем и делаем кинематическим
|
||
_rb.linearVelocity = Vector2.zero;
|
||
_rb.angularVelocity = 0f;
|
||
_rb.bodyType = RigidbodyType2D.Kinematic;
|
||
}
|
||
else
|
||
{
|
||
_rb.bodyType = RigidbodyType2D.Dynamic;
|
||
}
|
||
|
||
_col.enabled = true;
|
||
_col.isTrigger = true;
|
||
|
||
// Отвязываем от родителя
|
||
transform.SetParent(null, true);
|
||
bounceCounter = 0;
|
||
|
||
MoveJumpPad();
|
||
jumpPad.SetActive(true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Расчёт позиции и перемещение вспомогательной прыжковой платформы относительно снаряда.
|
||
/// Здесь используется приближённая геометрия, чтобы корректно позиционировать платформу даже при повороте снаряда.
|
||
/// </summary>
|
||
private void MoveJumpPad()
|
||
{
|
||
// Размеры коллайдера используются для определения смещения платформы
|
||
float sizeX = _col.bounds.size.y;
|
||
float sizeY = _col.bounds.size.x;
|
||
|
||
Vector3 jumpPadPos = transform.position;
|
||
|
||
// Прыжковая платформа размещается выше снаряда с учётом его поворота.
|
||
// Формула — приближённая (без учёта всех трансформаций), но даёт корректный результат в большинстве случаев.
|
||
float jumpPadY = Mathf.Sqrt(sizeX * sizeX * Mathf.Pow(Mathf.Cos(transform.localEulerAngles.z), 2) + sizeY * sizeY * Mathf.Pow(Mathf.Sin(transform.localEulerAngles.z), 2));
|
||
|
||
jumpPadPos.y = jumpPadPos.y + 0.5f * jumpPadY;
|
||
jumpPad.transform.position = jumpPadPos;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обработчик события прыжка с jumpPad — пытаемся подобрать снаряд игроком, которы<D180><D18B> прыгнул.
|
||
/// </summary>
|
||
/// <param name="playerObj">Игрок, совершивший прыжок</param>
|
||
private void OnPlayerJumped(GameObject playerObj)
|
||
{
|
||
TryPickup(playerObj);
|
||
}
|
||
|
||
#endregion
|
||
|
||
//
|
||
//
|
||
// -------------------------------------------
|
||
}
|
||
}
|
||
|