Added Feel plugin

This commit is contained in:
journaliciouz
2025-12-11 14:49:16 +01:00
parent 97dce4aaf6
commit 1942a531d4
2820 changed files with 257786 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,95 @@
fileFormatVersion: 2
guid: 671b69569d53c4c47ba2a89fb544082f
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 7
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Accessories/MMVision/ConeOfVisionAlpha.png
uploadId: 830868

View File

@@ -0,0 +1,70 @@
Shader "MoreMountains/ConeOfLight"
{
Properties
{
_MainTex("Diffuse Texture", 2D) = "white" {}
_Contrast("Contrast", Float) = 0.5
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"ForceNoShadowCasting" = "True"
"Queue" = "Transparent"
"RenderType" = "Transparent"
"IgnoreProjector" = "True"
}
Pass
{
ZTest Always
AlphaTest Greater 0.0
Blend DstColor One
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float _Contrast;
uniform float4 _Color;
struct VertexInput
{
float4 vertex : POSITION;
float4 uv : TEXCOORD0;
float4 color : COLOR;
};
struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
VertexOutput vert(VertexInput input)
{
VertexOutput output;
output.uv = input.uv;
output.color = input.color;
output.pos = UnityObjectToClipPos(input.vertex);
return output;
}
float4 frag(VertexOutput input) : COLOR
{
float4 diffuse = tex2D(_MainTex, input.uv);
diffuse.rgb = diffuse.rgb * _Color.rgb * input.color.rgb;
diffuse.rgb *= diffuse.a * _Color.a * input.color.a;
diffuse *= _Contrast;
return float4(diffuse);
}
ENDCG
}
}
}

View File

@@ -0,0 +1,16 @@
fileFormatVersion: 2
guid: 07e5069cdf9ee934f8d7b768c461452c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Accessories/MMVision/MMConeOfLight.shader
uploadId: 830868

View File

@@ -0,0 +1,265 @@
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
using System.Collections.Generic;
using System;
namespace MoreMountains.Tools
{
/// <summary>
/// This class will create a cone of vision defined by an angle and a distance around a point. It will look for targets within that field, and draw a mesh to show the cone of vision
/// initially inspired by this great tutorial by Sebastian Lague : https://www.youtube.com/watch?v=rQG9aUWarwE - check out his tutorials, they're amazing!
/// </summary>
[Serializable]
[AddComponentMenu("More Mountains/Tools/Vision/MM Cone Of Vision")]
public class MMConeOfVision : MonoBehaviour
{
/// <summary>
/// A struct to store raycast data
/// </summary>
public struct RaycastData
{
public bool Hit;
public Vector3 Point;
public float Distance;
public float Angle;
public RaycastData(bool hit, Vector3 point, float distance, float angle)
{
Hit = hit;
Point = point;
Distance = distance;
Angle = angle;
}
}
public struct MeshEdgePosition
{
public Vector3 PointA;
public Vector3 PointB;
public MeshEdgePosition(Vector3 pointA, Vector3 pointB)
{
PointA = pointA;
PointB = pointB;
}
}
[Header("Vision")]
public LayerMask ObstacleMask;
public float VisionRadius = 5f;
[Range(0f, 360f)]
public float VisionAngle = 20f;
[MMReadOnly]
public Vector3 Direction;
[MMReadOnly]
public Vector3 EulerAngles;
public Vector3 Offset;
[Header("Target scanning")]
public bool ShouldScanForTargets = true;
public LayerMask TargetMask;
public float ScanFrequencyInSeconds = 1f;
[MMReadOnly]
public List<Transform> VisibleTargets = new List<Transform>();
[Header("Mesh")]
public bool ShouldDrawMesh = true;
public float MeshDensity = 0.2f;
public int EdgePrecision = 3;
public float EdgeThreshold = 0.5f;
public MeshFilter VisionMeshFilter;
protected Mesh _visionMesh;
protected Collider[] _targetsWithinDistance;
protected Transform _target;
protected Vector3 _directionToTarget;
protected float _distanceToTarget;
protected float _lastScanTimestamp;
protected List<Vector3> _viewPoints = new List<Vector3>();
protected RaycastData _oldViewCast = new RaycastData();
protected RaycastData _viewCast = new RaycastData();
protected Vector3[] _vertices;
protected int[] _triangles;
protected Vector3 _minPoint, _maxPoint, _direction;
protected RaycastData _returnRaycastData;
protected RaycastHit _raycastAtAngleHit;
protected int _numberOfVerticesLastTime = 0;
public virtual Vector3 Center { get { return this.transform.position + Offset; } }
protected virtual void Awake()
{
_visionMesh = new Mesh();
if (ShouldDrawMesh)
{
VisionMeshFilter.mesh = _visionMesh;
}
}
protected virtual void LateUpdate()
{
if ((Time.time - _lastScanTimestamp > ScanFrequencyInSeconds) && ShouldScanForTargets)
{
ScanForTargets();
}
DrawMesh();
}
public virtual void SetDirectionAndAngles(Vector3 direction, Vector3 eulerAngles)
{
Direction = direction;
EulerAngles = eulerAngles;
}
protected virtual void ScanForTargets()
{
_lastScanTimestamp = Time.time;
VisibleTargets.Clear();
_targetsWithinDistance = Physics.OverlapSphere(Center, VisionRadius, TargetMask);
foreach (Collider collider in _targetsWithinDistance)
{
_target = collider.transform;
_directionToTarget = (_target.position - Center).normalized;
if (Vector3.Angle(Direction, _directionToTarget) < VisionAngle / 2f)
{
_distanceToTarget = Vector3.Distance(Center, _target.position);
bool duplicate = false;
foreach(Transform visibleTarget in VisibleTargets)
{
if (visibleTarget == _target)
{
duplicate = true;
}
}
if ((!Physics.Raycast(Center, _directionToTarget, _distanceToTarget, ObstacleMask)) && !duplicate)
{
VisibleTargets.Add(_target);
}
}
}
}
protected virtual void DrawMesh()
{
if (!ShouldDrawMesh)
{
return;
}
int steps = Mathf.RoundToInt(MeshDensity * VisionAngle);
float stepsAngle = VisionAngle / steps;
_viewPoints.Clear();
for (int i = 0; i <= steps; i++)
{
float angle = stepsAngle * i + EulerAngles.y - VisionAngle / 2f;
_viewCast = RaycastAtAngle(angle);
if (i > 0)
{
bool thresholdExceeded = Mathf.Abs(_oldViewCast.Distance - _viewCast.Distance) > EdgeThreshold;
if ((_oldViewCast.Hit != _viewCast.Hit) || (_oldViewCast.Hit && _viewCast.Hit && thresholdExceeded))
{
MeshEdgePosition edge = FindMeshEdgePosition(_oldViewCast, _viewCast);
if (edge.PointA != Vector3.zero)
{
_viewPoints.Add(edge.PointA);
}
if (edge.PointB != Vector3.zero)
{
_viewPoints.Add(edge.PointB);
}
}
}
_viewPoints.Add(_viewCast.Point);
_oldViewCast = _viewCast;
}
int numberOfVertices = _viewPoints.Count + 1;
if (numberOfVertices != _numberOfVerticesLastTime)
{
Array.Resize(ref _vertices, numberOfVertices);
Array.Resize(ref _triangles, (numberOfVertices - 2) * 3);
}
_vertices[0] = Offset;
for (int i = 0; i < numberOfVertices - 1; i++)
{
_vertices[i + 1] = this.transform.InverseTransformPoint(_viewPoints[i]);
if (i < numberOfVertices - 2)
{
_triangles[i * 3] = 0;
_triangles[i * 3 + 1] = i + 1;
_triangles[i * 3 + 2] = i + 2;
}
}
_visionMesh.Clear();
_visionMesh.vertices = _vertices;
_visionMesh.triangles = _triangles;
_visionMesh.RecalculateNormals();
_numberOfVerticesLastTime = numberOfVertices;
}
MeshEdgePosition FindMeshEdgePosition(RaycastData minimumViewCast, RaycastData maximumViewCast)
{
float minAngle = minimumViewCast.Angle;
float maxAngle = maximumViewCast.Angle;
_minPoint = minimumViewCast.Point;
_maxPoint = maximumViewCast.Point;
for (int i = 0; i < EdgePrecision; i++)
{
float angle = (minAngle + maxAngle) / 2;
RaycastData newViewCast = RaycastAtAngle(angle);
bool thresholdExceeded = Mathf.Abs(minimumViewCast.Distance - newViewCast.Distance) > EdgeThreshold;
if (newViewCast.Hit == minimumViewCast.Hit && !thresholdExceeded)
{
minAngle = angle;
_minPoint = newViewCast.Point;
}
else
{
maxAngle = angle;
_maxPoint = newViewCast.Point;
}
}
return new MeshEdgePosition(_minPoint, _maxPoint);
}
RaycastData RaycastAtAngle(float angle)
{
_direction = MMMaths.DirectionFromAngle(angle, 0f);
if (Physics.Raycast(Center, _direction, out _raycastAtAngleHit, VisionRadius, ObstacleMask))
{
_returnRaycastData.Hit = true;
_returnRaycastData.Point = _raycastAtAngleHit.point;
_returnRaycastData.Distance = _raycastAtAngleHit.distance;
_returnRaycastData.Angle = angle;
}
else
{
_returnRaycastData.Hit = false;
_returnRaycastData.Point = Center + _direction * VisionRadius;
_returnRaycastData.Distance = VisionRadius;
_returnRaycastData.Angle = angle;
}
return _returnRaycastData;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4fb9c8af0fcd0514e89d35554b6cae77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Accessories/MMVision/MMConeOfVision.cs
uploadId: 830868

View File

@@ -0,0 +1,269 @@
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
using System.Collections.Generic;
using System;
namespace MoreMountains.Tools
{
[Serializable]
[AddComponentMenu("More Mountains/Tools/Vision/MM Cone Of Vision 2D")]
public class MMConeOfVision2D : MonoBehaviour
{
public struct RaycastData
{
public bool Hit;
public Vector3 Point;
public float Distance;
public float Angle;
public RaycastData(bool hit, Vector3 point, float distance, float angle)
{
Hit = hit;
Point = point;
Distance = distance;
Angle = angle;
}
}
public struct MeshEdgePosition
{
public Vector3 PointA;
public Vector3 PointB;
public MeshEdgePosition(Vector3 pointA, Vector3 pointB)
{
PointA = pointA;
PointB = pointB;
}
}
[Header("Vision")]
public LayerMask ObstacleMask;
public float VisionRadius = 5f;
[Range(0f, 360f)]
public float VisionAngle = 20f;
[Range(0f, 360f)]
public float AngleOffset = 0f;
[MMReadOnly]
public Vector3 Direction;
[MMReadOnly]
public Vector3 EulerAngles;
[Header("Target scanning")]
public bool ShouldScanForTargets = true;
public LayerMask TargetMask;
public float ScanFrequencyInSeconds = 1f;
[MMReadOnly]
public List<Transform> VisibleTargets = new List<Transform>();
[Header("Mesh")]
public bool ShouldDrawMesh = true;
public float MeshDensity = 0.2f;
public int EdgePrecision = 3;
public float EdgeThreshold = 0.5f;
public MeshFilter VisionMeshFilter;
#if MM_PHYSICS2D
protected Mesh _visionMesh;
protected Collider2D[] _targetsWithinDistance;
protected Transform _target;
protected Vector3 _directionToTarget;
protected float _distanceToTarget;
protected float _lastScanTimestamp;
protected RaycastHit2D _scanForTargetsHit2D;
protected List<Vector3> _viewPoints = new List<Vector3>();
protected RaycastData _oldViewCast = new RaycastData();
protected RaycastData _viewCast = new RaycastData();
protected Vector3[] _vertices;
protected int[] _triangles;
protected Vector3 _minPoint, _maxPoint, _direction;
protected RaycastData _returnRaycastData;
protected RaycastHit2D _raycastAtAngleHit2D;
protected int _numberOfVerticesLastTime = 0;
protected virtual void Awake()
{
_visionMesh = new Mesh();
Direction = Vector3.right;
EulerAngles = Vector3.zero;
if (ShouldDrawMesh)
{
VisionMeshFilter.mesh = _visionMesh;
}
SetDirectionAndAngles(Direction, EulerAngles);
}
protected void OnValidate()
{
EulerAngles = Vector3.zero;
SetDirectionAndAngles(Direction, EulerAngles);
}
protected virtual void LateUpdate()
{
if ((Time.time - _lastScanTimestamp > ScanFrequencyInSeconds) && ShouldScanForTargets)
{
ScanForTargets();
}
DrawMesh();
}
public virtual void SetDirectionAndAngles(Vector3 direction, Vector3 eulerAngles)
{
Direction = direction;
EulerAngles = eulerAngles;
EulerAngles.y += AngleOffset;
}
protected virtual void ScanForTargets()
{
_lastScanTimestamp = Time.time;
VisibleTargets.Clear();
_targetsWithinDistance = Physics2D.OverlapCircleAll(this.transform.position, VisionRadius, TargetMask);
foreach (Collider2D collider in _targetsWithinDistance)
{
_target = collider.transform;
_directionToTarget = (_target.position - this.transform.position).normalized;
if (Vector3.Angle(Direction, _directionToTarget) < VisionAngle / 2f)
{
_distanceToTarget = Vector3.Distance(this.transform.position, _target.position);
_scanForTargetsHit2D = Physics2D.Raycast(this.transform.position, _directionToTarget, _distanceToTarget, ObstacleMask);
if (!_scanForTargetsHit2D)
{
VisibleTargets.Add(_target);
}
}
}
}
protected virtual void DrawMesh()
{
if (!ShouldDrawMesh)
{
return;
}
int steps = Mathf.RoundToInt(MeshDensity * VisionAngle);
float stepsAngle = VisionAngle / steps;
_viewPoints.Clear();
for (int i = 0; i <= steps; i++)
{
float angle = stepsAngle * i + EulerAngles.y - VisionAngle / 2f;
_viewCast = RaycastAtAngle(angle);
if (i > 0)
{
bool thresholdExceeded = Mathf.Abs(_oldViewCast.Distance - _viewCast.Distance) > EdgeThreshold;
if ((_oldViewCast.Hit != _viewCast.Hit) || (_oldViewCast.Hit && _viewCast.Hit && thresholdExceeded))
{
MeshEdgePosition edge = FindMeshEdgePosition(_oldViewCast, _viewCast);
if (edge.PointA != Vector3.zero)
{
_viewPoints.Add(edge.PointA);
}
if (edge.PointB != Vector3.zero)
{
_viewPoints.Add(edge.PointB);
}
}
}
_viewPoints.Add(_viewCast.Point);
_oldViewCast = _viewCast;
}
int numberOfVertices = _viewPoints.Count + 1;
if (numberOfVertices != _numberOfVerticesLastTime)
{
Array.Resize(ref _vertices, numberOfVertices);
Array.Resize(ref _triangles, (numberOfVertices - 2) * 3);
}
_vertices[0].x = 0;
_vertices[0].y = 0;
_vertices[0].z = 0;
for (int i = 0; i < numberOfVertices - 1; i++)
{
_vertices[i + 1] = this.transform.InverseTransformPoint(_viewPoints[i]);
if (i < numberOfVertices - 2)
{
_triangles[i * 3] = 0;
_triangles[i * 3 + 1] = i + 1;
_triangles[i * 3 + 2] = i + 2;
}
}
_visionMesh.Clear();
_visionMesh.vertices = _vertices;
_visionMesh.triangles = _triangles;
_visionMesh.RecalculateNormals();
_numberOfVerticesLastTime = numberOfVertices;
}
MeshEdgePosition FindMeshEdgePosition(RaycastData minimumViewCast, RaycastData maximumViewCast)
{
float minAngle = minimumViewCast.Angle;
float maxAngle = maximumViewCast.Angle;
_minPoint = minimumViewCast.Point;
_maxPoint = maximumViewCast.Point;
for (int i = 0; i < EdgePrecision; i++)
{
float angle = (minAngle + maxAngle) / 2;
RaycastData newViewCast = RaycastAtAngle(angle);
bool thresholdExceeded = Mathf.Abs(minimumViewCast.Distance - newViewCast.Distance) > EdgeThreshold;
if (newViewCast.Hit == minimumViewCast.Hit && !thresholdExceeded)
{
minAngle = angle;
_minPoint = newViewCast.Point;
}
else
{
maxAngle = angle;
_maxPoint = newViewCast.Point;
}
}
return new MeshEdgePosition(_minPoint, _maxPoint);
}
RaycastData RaycastAtAngle(float angle)
{
_direction = MMMaths.DirectionFromAngle2D(angle, 0f);
_raycastAtAngleHit2D = Physics2D.Raycast(this.transform.position, _direction, VisionRadius, ObstacleMask);
if (_raycastAtAngleHit2D)
{
_returnRaycastData.Hit = true;
_returnRaycastData.Point = _raycastAtAngleHit2D.point;
_returnRaycastData.Distance = _raycastAtAngleHit2D.distance;
_returnRaycastData.Angle = angle;
}
else
{
_returnRaycastData.Hit = false;
_returnRaycastData.Point = this.transform.position + _direction * VisionRadius;
_returnRaycastData.Distance = VisionRadius;
_returnRaycastData.Angle = angle;
}
return _returnRaycastData;
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ceea5d59c11750f4e8deb2c47ff8bcb4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Accessories/MMVision/MMConeOfVision2D.cs
uploadId: 830868