гигантский ассет
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
using UnityEngine;
|
||||
using UHFPS.Tools;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UHFPS.Runtime
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody), typeof(AudioSource))]
|
||||
public class DraggableItem : SaveableBehaviour, IOnDragStart, IOnDragEnd
|
||||
{
|
||||
[Tooltip("Minimum and maximum distance to which the object can be zoomed.")]
|
||||
public MinMax ZoomDistance;
|
||||
[Tooltip("Maximum hold distance at which the object will be out of range and will be dropped.")]
|
||||
public float MaxHoldDistance = 4f;
|
||||
|
||||
public bool EnableImpactSound = true;
|
||||
[Tooltip("Array of the impact sounds.")]
|
||||
public AudioClip[] ImpactSounds;
|
||||
[Tooltip("Minimum and maximum impact volume. The impact will be played if the calculated volume is greater than the minimum impact volume.")]
|
||||
public MinMax ImpactVolume;
|
||||
[Tooltip("Modifier that is multiplied with the impact volume. Higher value = louder impact volume")]
|
||||
public float VolumeModifier;
|
||||
[Tooltip("Time at which the next impact will be detected.")]
|
||||
public float NextImpact = 0.1f;
|
||||
|
||||
public bool EnableSlidingSound = true;
|
||||
[Tooltip("Minimum angle between the collision and the motion at which the sliding is detected. Near 0 = sliding, More than 0 = static")]
|
||||
public float MinSlidingFactor = 5f;
|
||||
[Tooltip("Velocity range at which the sliding volume is calculated. Higher value = faster movement is required to achieve volume 1")]
|
||||
public float SlidingVelocityRange = 5f;
|
||||
[Tooltip("Modifier that is multiplied with the sliding volume. Higher value = louder sliding volume")]
|
||||
public float SlidingVolumeModifier = 5f;
|
||||
[Tooltip("Speed at which the volume is faded when the sliding stops.")]
|
||||
public float VolumeFadeOffSpeed = 5f;
|
||||
|
||||
public UnityEvent OnDragStarted;
|
||||
public UnityEvent OnDragEnded;
|
||||
|
||||
public bool Collision;
|
||||
|
||||
private Rigidbody rigid;
|
||||
private AudioSource audioSource;
|
||||
|
||||
private float impactTime;
|
||||
private int lastImpact;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rigid = GetComponent<Rigidbody>();
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
audioSource.volume = 0f;
|
||||
audioSource.loop = true;
|
||||
audioSource.spatialBlend = 1f;
|
||||
audioSource.playOnAwake = false;
|
||||
}
|
||||
|
||||
private void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
Collision = true;
|
||||
if (!EnableImpactSound) return;
|
||||
|
||||
float newVolume = collision.relativeVelocity.magnitude / VolumeModifier;
|
||||
if (newVolume < ImpactVolume.RealMin) return;
|
||||
|
||||
newVolume = Mathf.Clamp(newVolume, ImpactVolume.RealMin, ImpactVolume.RealMax);
|
||||
if (impactTime <= 0) OnObjectImpact(newVolume);
|
||||
}
|
||||
|
||||
private void OnCollisionExit(Collision collision)
|
||||
{
|
||||
Collision = false;
|
||||
}
|
||||
|
||||
private void OnCollisionStay(Collision collision)
|
||||
{
|
||||
Collision = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (impactTime > 0) impactTime -= Time.deltaTime;
|
||||
if (!EnableSlidingSound) return;
|
||||
|
||||
float velMagnitude = rigid.linearVelocity.magnitude;
|
||||
float velMagnitudeNormalized = rigid.linearVelocity.normalized.magnitude;
|
||||
|
||||
if (Collision && velMagnitudeNormalized > MinSlidingFactor)
|
||||
{
|
||||
float slidingVolume = Mathf.InverseLerp(0f, SlidingVelocityRange, velMagnitude);
|
||||
if (!audioSource.isPlaying) audioSource.Play();
|
||||
audioSource.volume = Mathf.Clamp01(slidingVolume * SlidingVolumeModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSource.volume = Mathf.MoveTowards(audioSource.volume, 0f, Time.deltaTime * VolumeFadeOffSpeed);
|
||||
if (audioSource.isPlaying && audioSource.volume <= 0) audioSource.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnObjectImpact(float volume)
|
||||
{
|
||||
lastImpact = GameTools.RandomUnique(0, ImpactSounds.Length, lastImpact);
|
||||
AudioClip audioClip = ImpactSounds[lastImpact];
|
||||
AudioSource.PlayClipAtPoint(audioClip, transform.position, volume);
|
||||
}
|
||||
|
||||
public void OnDragStart()
|
||||
{
|
||||
OnDragStarted?.Invoke();
|
||||
}
|
||||
|
||||
public void OnDragEnd()
|
||||
{
|
||||
OnDragEnded?.Invoke();
|
||||
}
|
||||
|
||||
public override StorableCollection OnSave()
|
||||
{
|
||||
return new StorableCollection().WithTransform(transform);
|
||||
}
|
||||
|
||||
public override void OnLoad(JToken data)
|
||||
{
|
||||
data.LoadTransform(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7ef03693823c604f94c979641ed99eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UHFPS.Tools;
|
||||
using ThunderWire.Attributes;
|
||||
|
||||
namespace UHFPS.Runtime
|
||||
{
|
||||
[Docs("https://docs.twgamesdev.com/uhfps/guides/interactions")]
|
||||
public class InteractableItem : SaveableBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class Hotspot
|
||||
{
|
||||
public Transform HotspotTransform;
|
||||
[Tooltip("If you want the hotspot to be shown, keep this value true.")]
|
||||
public bool Enabled = true;
|
||||
[Tooltip("If this option is enabled, the hotspot action will be called when you put back the examined item.")]
|
||||
public bool ResetHotspot = false;
|
||||
[Space] public UnityEvent HotspotAction;
|
||||
}
|
||||
|
||||
public enum InteractableTypeEnum { GenericItem, InventoryItem, ExamineItem, InventoryExpand }
|
||||
public enum MessageTypeEnum { None, Hint, Alert }
|
||||
public enum ExamineTypeEnum { None, GenericObject, CustomObject }
|
||||
public enum ExamineRotateEnum { Static, Horizontal, Vertical, Both }
|
||||
public enum DisableTypeEnum { None, Deactivate, Destroy }
|
||||
|
||||
public InteractableTypeEnum InteractableType = InteractableTypeEnum.GenericItem;
|
||||
public MessageTypeEnum MessageType = MessageTypeEnum.None;
|
||||
public ExamineTypeEnum ExamineType = ExamineTypeEnum.None;
|
||||
public ExamineRotateEnum ExamineRotate = ExamineRotateEnum.Static;
|
||||
public DisableTypeEnum DisableType = DisableTypeEnum.None;
|
||||
|
||||
public ItemProperty PickupItem;
|
||||
public ItemCustomData ItemCustomData;
|
||||
|
||||
public GString InteractTitle;
|
||||
public GString ExamineTitle;
|
||||
|
||||
public GString PaperText;
|
||||
public GString HintMessage;
|
||||
|
||||
public float MessageTime = 3f;
|
||||
|
||||
public ushort Quantity = 1;
|
||||
public ushort SlotsToExpand = 1;
|
||||
public bool ExpandRows;
|
||||
|
||||
public bool UseInventoryTitle = true;
|
||||
public bool ExamineInventoryTitle = true;
|
||||
public bool ShowExamineTitle = true;
|
||||
|
||||
public bool ShowFloatingIcon = false;
|
||||
public bool TakeFromExamine = false;
|
||||
public bool AllowCursorExamine = false;
|
||||
public bool IsPaper = false;
|
||||
public bool AutoShortcut = false;
|
||||
|
||||
public bool UseExamineZooming = true;
|
||||
public MinMax ExamineZoomLimits = new(0.3f, 0.4f);
|
||||
public float ExamineDistance = 0.4f;
|
||||
|
||||
public bool UseFaceRotation;
|
||||
public Vector3 FaceRotation;
|
||||
|
||||
public bool UseControlPoint;
|
||||
public Vector3 ControlPoint = new(0, 0.1f, 0);
|
||||
|
||||
public List<Collider> CollidersEnable = new();
|
||||
public List<Collider> CollidersDisable = new();
|
||||
public Hotspot ExamineHotspot = new();
|
||||
|
||||
public SoundClip PickupSound;
|
||||
public SoundClip ExamineSound;
|
||||
public SoundClip ExamineHintSound;
|
||||
|
||||
public UnityEvent OnTakeEvent;
|
||||
public UnityEvent OnExamineStartEvent;
|
||||
public UnityEvent OnExamineEndEvent;
|
||||
|
||||
public bool IsExamined;
|
||||
|
||||
public bool IsCustomExamine => InteractableType != InteractableTypeEnum.GenericItem && ExamineType == ExamineTypeEnum.CustomObject;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the item.
|
||||
/// </summary>
|
||||
public string ItemName
|
||||
{
|
||||
get
|
||||
{
|
||||
string title = InteractTitle;
|
||||
|
||||
if (InteractableType == InteractableTypeEnum.InventoryItem && UseInventoryTitle)
|
||||
{
|
||||
title = PickupItem.GetItem().Title;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if(IsCustomExamine)
|
||||
{
|
||||
foreach (var col in CollidersEnable)
|
||||
{
|
||||
col.enabled = false;
|
||||
}
|
||||
|
||||
foreach (var col in CollidersDisable)
|
||||
{
|
||||
col.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (InteractableType != InteractableTypeEnum.InventoryItem || !UseInventoryTitle)
|
||||
InteractTitle.SubscribeGloc();
|
||||
|
||||
if(ExamineType != ExamineTypeEnum.None)
|
||||
ExamineTitle.SubscribeGloc();
|
||||
|
||||
if(ExamineType != ExamineTypeEnum.None && IsPaper)
|
||||
PaperText.SubscribeGloc();
|
||||
|
||||
if(MessageType == MessageTypeEnum.Hint)
|
||||
HintMessage.SubscribeGloc();
|
||||
}
|
||||
|
||||
public void OnInteract()
|
||||
{
|
||||
if (InteractableType == InteractableTypeEnum.ExamineItem)
|
||||
return;
|
||||
|
||||
GameTools.PlayOneShot2D(transform.position, PickupSound, "PickupSound");
|
||||
OnTakeEvent?.Invoke();
|
||||
|
||||
if (DisableType != DisableTypeEnum.None)
|
||||
EnabledState(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to interact with the object as if you were pressing the USE button on it.
|
||||
/// </summary>
|
||||
public void InteractWithObject()
|
||||
{
|
||||
if (InteractableType == InteractableTypeEnum.ExamineItem)
|
||||
return;
|
||||
|
||||
PlayerPresenceManager.Instance.PlayerManager.InteractController.Interact(gameObject);
|
||||
}
|
||||
|
||||
public void EnabledState(bool enabled)
|
||||
{
|
||||
if (!enabled && SaveGameManager.HasReference)
|
||||
SaveGameManager.RemoveSaveable(gameObject);
|
||||
|
||||
if(DisableType == DisableTypeEnum.Deactivate)
|
||||
gameObject.SetActive(enabled);
|
||||
else if(DisableType == DisableTypeEnum.Destroy && !enabled)
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
public override StorableCollection OnSave()
|
||||
{
|
||||
return new StorableCollection()
|
||||
{
|
||||
{ "position", transform.position.ToSaveable() },
|
||||
{ "rotation", transform.eulerAngles.ToSaveable() },
|
||||
{ "quantity", Quantity },
|
||||
{ "enabledState", gameObject.activeSelf },
|
||||
{ "hotspotEnabled", ExamineHotspot.Enabled },
|
||||
{ "customData", ItemCustomData.GetJson() }
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnLoad(JToken data)
|
||||
{
|
||||
transform.position = data["position"].ToObject<Vector3>();
|
||||
transform.eulerAngles = data["rotation"].ToObject<Vector3>();
|
||||
|
||||
Quantity = (ushort)data["quantity"];
|
||||
EnabledState((bool)data["enabledState"]);
|
||||
ExamineHotspot.Enabled = (bool)data["hotspotEnabled"];
|
||||
|
||||
ItemCustomData.JsonData = data["customData"].ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1193cc42578b51349ae91df02822e87c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,132 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UHFPS.Input;
|
||||
using UHFPS.Tools;
|
||||
|
||||
namespace UHFPS.Runtime
|
||||
{
|
||||
public class MovableObject : MonoBehaviour, IStateInteract
|
||||
{
|
||||
public enum MoveDirectionEnum { LeftRight, ForwardBackward, AllDirections }
|
||||
|
||||
public AudioSource AudioSource;
|
||||
public Rigidbody Rigidbody;
|
||||
public Axis ForwardAxis;
|
||||
public bool DrawGizmos = true;
|
||||
|
||||
public MoveDirectionEnum MoveDirection;
|
||||
public LayerMask CollisionMask;
|
||||
public Vector3 HoldOffset;
|
||||
public bool AllowRotation = true;
|
||||
|
||||
public float HoldDistance = 2f;
|
||||
public float ObjectWeight = 20f;
|
||||
public float PlayerRadius = 0.3f;
|
||||
public float PlayerHeight = 1.8f;
|
||||
public float PlayerFeetOffset = 0f;
|
||||
|
||||
public float WalkMultiplier = 1f;
|
||||
public float LookMultiplier = 1f;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
public float SlideVolume = 1f;
|
||||
public float VolumeFadeSpeed = 1f;
|
||||
|
||||
public bool UseMouseLimits;
|
||||
public MinMax MouseVerticalLimits;
|
||||
|
||||
public Transform RootMovable => Rigidbody.transform;
|
||||
|
||||
public MeshRenderer Renderer => RootMovable.GetComponent<MeshRenderer>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if(Rigidbody != null) Rigidbody.mass = ObjectWeight;
|
||||
if(AudioSource != null)
|
||||
{
|
||||
AudioSource.playOnAwake = false;
|
||||
AudioSource.spatialBlend = 1f;
|
||||
AudioSource.loop = true;
|
||||
AudioSource.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeSoundOut()
|
||||
{
|
||||
StartCoroutine(FadeSound());
|
||||
}
|
||||
|
||||
IEnumerator FadeSound()
|
||||
{
|
||||
while(Mathf.Approximately(AudioSource.volume, 0f))
|
||||
{
|
||||
AudioSource.volume = Mathf.MoveTowards(AudioSource.volume, 0f, Time.deltaTime * SlideVolume * 10);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
AudioSource.volume = 0f;
|
||||
AudioSource.Stop();
|
||||
}
|
||||
|
||||
public StateParams OnStateInteract()
|
||||
{
|
||||
if (!CheckOverlapping())
|
||||
{
|
||||
StopAllCoroutines();
|
||||
return new StateParams()
|
||||
{
|
||||
stateKey = PlayerStateMachine.PUSHING_STATE,
|
||||
stateData = new StorableCollection()
|
||||
{
|
||||
{ "reference", this }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool CheckOverlapping()
|
||||
{
|
||||
Vector3 forwardGlobal = ForwardAxis.Convert();
|
||||
float height = PlayerHeight - 0.6f;
|
||||
|
||||
Vector3 position = RootMovable.TransformPoint((-forwardGlobal * HoldDistance) + HoldOffset);
|
||||
Vector3 bottomPos = new(position.x, Renderer.bounds.min.y, position.z);
|
||||
|
||||
Vector3 playerBottom = bottomPos;
|
||||
playerBottom.y += PlayerFeetOffset;
|
||||
|
||||
Vector3 p1 = new Vector3(position.x, playerBottom.y, position.z);
|
||||
Vector3 p2 = new Vector3(position.x, playerBottom.y + height, position.z);
|
||||
|
||||
return Physics.CheckCapsule(p1, p2, PlayerRadius, CollisionMask);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!DrawGizmos)
|
||||
return;
|
||||
|
||||
Vector3 forwardGlobal = ForwardAxis.Convert();
|
||||
Vector3 forwardLocal = RootMovable.Direction(ForwardAxis);
|
||||
float radius = 0.5f;
|
||||
|
||||
Vector3 position = RootMovable.TransformPoint((-forwardGlobal * HoldDistance) + HoldOffset);
|
||||
Vector3 bottomPos = new(position.x, Renderer.bounds.min.y, position.z);
|
||||
|
||||
GizmosE.DrawDisc(bottomPos, radius, Color.green, Color.green.Alpha(0.01f));
|
||||
GizmosE.DrawGizmosArrow(bottomPos, forwardLocal * radius);
|
||||
|
||||
float height = PlayerHeight - 0.6f;
|
||||
Vector3 playerBottom = bottomPos;
|
||||
playerBottom.y += PlayerFeetOffset;
|
||||
|
||||
Vector3 p1 = new(position.x, playerBottom.y, position.z);
|
||||
Vector3 p2 = new(position.x, playerBottom.y + height, position.z);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
GizmosE.DrawWireCapsule(p1, p2, PlayerRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa8537c177a53574eaed5557887213ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user