备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View File

@@ -0,0 +1,419 @@
using CW.Common;
using PaintCore;
using PaintIn3D;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class BHitScreen : BHitScreenBase
{
// This stores extra information for each finger unique to this component
protected class Link : CwInputManager.Link
{
public float Age;
public bool Down;
public int State;
public float Distance;
public Vector2 ScreenDelta;
public Vector2 ScreenOld;
public List<Vector2> History = new List<Vector2>();
public void Move(Vector2 screenNew)
{
if (State == 0)
{
ScreenOld = screenNew;
State = 1;
}
else
{
if (TryMove(screenNew) == true || State == 2)
{
State += 1;
}
}
}
private bool TryMove(Vector2 screenNew)
{
var threshold = 2.0f;
var distance = Vector2.Distance(ScreenOld, screenNew);
if (distance >= threshold)
{
ScreenOld = Vector2.MoveTowards(ScreenOld, screenNew, distance - threshold * 0.5f);
return true;
}
return false;
}
public override void Clear()
{
Age = 0.0f;
Down = false;
State = 0;
Distance = 0.0f;
ScreenDelta = Vector2.zero;
ScreenOld = Vector2.zero;
History.Clear();
}
}
public enum FrequencyType
{
PixelInterval,
ScaledPixelInterval,
TimeInterval,
OnceOnRelease,
OnceOnPress,
OnceEveryFrame
}
/// <summary>This allows you to control how often the screen is painted.
/// PixelInterval = Once every <b>Interval</b> pixels.
/// ScaledPixelInterval = Like <b>PixelInterval</b>, but scaled to the screen DPI.
/// TimeInterval = Once every <b>Interval</b> seconds.
/// OnceOnRelease = When the finger/mouse goes down a preview will be shown, and when it goes up the paint will apply.
/// OnceOnPress = When the finger/mouse goes down the paint will apply.
/// OnceEveryFrame = Every frame the paint will apply.</summary>
public FrequencyType Frequency { set { frequency = value; } get { return frequency; } }
[SerializeField] private FrequencyType frequency = FrequencyType.OnceEveryFrame;
/// <summary>This allows you to set the pixels/seconds between each hit point based on the current <b>Frequency</b> setting.</summary>
public float Interval { set { interval = value; } get { return interval; } }
[SerializeField] private float interval = 10.0f;
/// <summary>This allows you to connect the hit points together to form lines.</summary>
public CwPointConnector Connector { get { if (connector == null) connector = new CwPointConnector(); return connector; } }
[SerializeField] private CwPointConnector connector;
[System.NonSerialized]
private List<Link> links = new List<Link>();
/// <summary>This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.</summary>
[ContextMenu("Clear Hit Cache")]
public void ClearHitCache()
{
Connector.ClearHitCache();
}
/// <summary>If this GameObject has teleported and you have <b>ConnectHits</b> or <b>HitSpacing</b> enabled, then you can call this to prevent a line being drawn between the previous and current points.</summary>
[ContextMenu("Reset Connections")]
public void ResetConnections()
{
connector.ResetConnections();
}
protected virtual void OnEnable()
{
foreach (var link in links)
{
link.Clear();
}
Connector.ResetConnections();
if (ShouldUpgradePointers() == true)
{
Debug.LogWarning("Upgrading CwHitScreen Controls - To remove this warning you can manually click the \"Upgrade\" button in the inspector of this component while outside of play mode.", gameObject);
TryUpgradePointers();
}
}
protected virtual void Update()
{
connector.Update();
}
public override void BreakFinger(CwInputManager.Finger finger)
{
var link = CwInputManager.Link.Find(links, finger);
if (link != null)
{
connector.BreakHits(link);
}
}
public override void HandleFingerUpdate(CwInputManager.Finger finger, bool down, bool up)
{
var link = CwInputManager.Link.Find(links, finger);
var set = true;
if (finger.Index < 0) // Preview?
{
if (CwInputManager.PointOverGui(finger.ScreenPosition, GuiLayers) == true)
{
connector.BreakHits(link);
return;
}
}
else
{
if (down == true)
{
if (CwInputManager.PointOverGui(finger.ScreenPosition, GuiLayers) == true)
{
connector.BreakHits(link);
return;
}
}
else
{
if (link == null)
{
return;
}
}
}
if (link == null)
{
link = CwInputManager.Link.Create(ref links, finger);
}
link.Move(finger.ScreenPosition);
if (finger.Index < 0) // Preview?
{
RecordAndPaintAt(link, finger.ScreenPosition, link.ScreenOld, true, 0.0f, link);
return;
}
if (NeedsDrawAngle == true)
{
down = link.State == 2;
set = link.State >= 2;
}
if (set == true)
{
switch (frequency)
{
case FrequencyType.PixelInterval: PaintSmooth(link, down, interval); break;
case FrequencyType.ScaledPixelInterval: PaintSmooth(link, down, interval / CwInputManager.ScaleFactor); break;
case FrequencyType.TimeInterval: PaintInterval(link, down); break;
case FrequencyType.OnceOnRelease: PaintRelease(link, up); break;
case FrequencyType.OnceOnPress: PaintPress(link, down); break;
case FrequencyType.OnceEveryFrame: PaintEvery(link, down); break;
}
}
base.HandleFingerUpdate(finger, down, up);
}
protected override void HandleFingerUp(CwInputManager.Finger finger)
{
var link = CwInputManager.Link.Find(links, finger);
if (link != null)
{
connector.BreakHits(link);
OnFingerUp(link);
link.Clear();
}
}
private void PaintSmooth(Link link, bool down, float pixelSpacing)
{
var head = link.Finger.GetSmoothScreenPosition(0.0f);
if (down == true || link.History.Count == 0)
{
if (storeStates == true)
{
CwStateManager.PotentiallyStoreAllStates();
}
RecordAndPaintAt(link, link.Finger.ScreenPosition, link.ScreenOld, false, link.Finger.Pressure, link);
}
if (pixelSpacing > 0.0f)
{
var steps = Mathf.Max(1, Mathf.FloorToInt(link.Finger.SmoothScreenPositionDelta));
var step = CwHelper.Reciprocal(steps);
for (var i = 0; i <= steps; i++)
{
var next = link.Finger.GetSmoothScreenPosition(Mathf.Clamp01(i * step));
var dist = Vector2.Distance(head, next);
var gaps = Mathf.FloorToInt((link.Distance + dist) / pixelSpacing);
for (var j = 0; j < gaps; j++)
{
var remainder = pixelSpacing - link.Distance;
head = Vector2.MoveTowards(head, next, remainder);
RecordAndPaintAt(link, head, link.History[link.History.Count - 1], false, link.Finger.Pressure, link);
dist -= remainder;
link.Distance = 0.0f;
}
link.Distance += dist;
head = next;
}
}
}
protected virtual void OnFingerUp(Link link)
{
}
private void PaintInterval(Link link, bool down)
{
if (down == true)
{
if (storeStates == true)
{
CwStateManager.PotentiallyStoreAllStates();
}
link.Age = interval;
}
link.Age += Time.deltaTime;
if (link.Age >= interval)
{
if (interval > 0.0f)
{
link.Age %= interval;
}
else
{
link.Age = 0.0f;
}
RecordAndPaintAt(link, link.Finger.ScreenPosition, link.ScreenOld, false, link.Finger.Pressure, link);
}
}
private void PaintRelease(Link link, bool up)
{
var preview = true;
if (up == true)
{
preview = false;
if (storeStates == true)
{
CwStateManager.PotentiallyStoreAllStates();
}
}
RecordAndPaintAt(link, link.Finger.ScreenPosition, link.ScreenOld, preview, link.Finger.Pressure, link);
}
private void PaintPress(Link link, bool down)
{
if (down == true)
{
if (storeStates == true)
{
CwStateManager.PotentiallyStoreAllStates();
}
RecordAndPaintAt(link, link.Finger.ScreenPosition, link.ScreenOld, false, link.Finger.Pressure, link);
}
}
private void PaintEvery(Link link, bool down)
{
if (down == true)
{
if (storeStates == true)
{
CwStateManager.PotentiallyStoreAllStates();
}
}
RecordAndPaintAt(link, link.Finger.ScreenPosition, link.ScreenOld, false, link.Finger.Pressure, link);
}
private void RecordAndPaintAt(Link link, Vector2 screenNew, Vector2 screenOld, bool preview, float pressure, object owner)
{
link.History.Add(screenNew);
PaintAt(connector, connector.HitCache, screenNew, screenOld, preview, pressure, owner);
}
}
}
#if UNITY_EDITOR
namespace game
{
using UnityEditor;
using TARGET = BHitScreen;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class BHitScreen_Editor : CwHitScreenBase_Editor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
base.OnInspector();
Separator();
DrawBasic();
Separator();
DrawAdvancedFoldout();
Separator();
var point = tgt.Emit == BHitScreenBase.EmitType.PointsIn3D;
var line = tgt.Emit == BHitScreenBase.EmitType.PointsIn3D && tgt.Connector.ConnectHits == true;
var triangle = tgt.Emit == BHitScreenBase.EmitType.TrianglesIn3D;
var coord = tgt.Emit == BHitScreenBase.EmitType.PointsOnUV;
tgt.Connector.HitCache.Inspector(tgt.gameObject, point: point, line: line, triangle: triangle, coord: coord);
}
protected override void DrawBasic()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
base.DrawBasic();
Draw("frequency", "This allows you to control how often the screen is painted.\n\nPixelInterval = Once every Interval pixels.\n\nScaledPixelInterval = Once every Interval scaled pixels.\n\nTimeInterval = Once every Interval seconds.\n\nOnceOnRelease = When the finger/mouse goes down a preview will be shown, and when it goes up the paint will apply.\n\nOnceOnPress = When the finger/mouse goes down the paint will apply.\n\nOnceEveryFrame = Every frame the paint will apply.");
if (Any(tgts, t => t.Frequency == BHitScreen.FrequencyType.PixelInterval || t.Frequency == BHitScreen.FrequencyType.ScaledPixelInterval || t.Frequency == BHitScreen.FrequencyType.TimeInterval))
{
BeginIndent();
BeginError(Any(tgts, t => t.Interval <= 0.0f));
Draw("interval", "This allows you to set the pixels/seconds between each hit point based on the current Frequency setting.");
EndError();
EndIndent();
}
}
protected override void DrawAdvanced()
{
base.DrawAdvanced();
Separator();
CwPointConnector_Editor.Draw();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 623b6c15fb081f3408bd04cbf9b5e520
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,360 @@
using CW.Common;
using PaintCore;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
/// <summary>This class contains common code for screen based mouse/finger hit components.</summary>
public abstract class BHitScreenBase : CwHitPointers
{
public enum RotationType
{
Normal,
World,
ThisRotation,
ThisLocalRotation,
CustomRotation,
CustomLocalRotation
}
public enum RelativeType
{
WorldUp,
CameraUp,
DrawAngle
}
public enum DirectionType
{
HitNormal,
RayDirection,
CameraDirection
}
public enum EmitType
{
PointsIn3D = 0,
PointsOnUV = 20,
TrianglesIn3D = 30
}
[System.Flags]
public enum ButtonTypes
{
LeftMouse = 1 << 0,
RightMouse = 1 << 1,
MiddleMouse = 1 << 2,
Touch = 1 << 5
}
/// <summary>Orient to a specific camera?
/// None = MainCamera.</summary>
public Camera Camera { set { _camera = value; } get { return _camera; } }
[SerializeField] private Camera _camera;
/// <summary>The layers you want the raycast to hit.</summary>
public LayerMask Layers { set { layers = value; } get { return layers; } }
[SerializeField] private LayerMask layers = Physics.DefaultRaycastLayers;
/// <summary>This allows you to control the hit data this component sends out.
/// PointsIn3D = Point drawing in 3D.
/// PointsOnUV = Point drawing on UV (requires non-convex <b>MeshCollider</b>).
/// TrianglesIn3D = Triangle drawing in 3D (requires non-convex <b>MeshCollider</b>).</summary>
public EmitType Emit { set { emit = value; } get { return emit; } }
[UnityEngine.Serialization.FormerlySerializedAs("draw")][SerializeField] private EmitType emit;
/// <summary>This allows you to control how the paint is rotated.
/// Normal = The rotation will be based on a normal direction, and rolled relative to an up axis.
/// World = The rotation will be aligned to the world, or given no rotation.
/// ThisRotation = The current <b>Transform.rotation</b> will be used.
/// ThisLocalRotation = The current <b>Transform.localRotation</b> will be used.
/// CustomRotation = The specified <b>CustomTransform.rotation</b> will be used.
/// CustomLocalRotation = The specified <b>CustomTransform.localRotation</b> will be used.</summary>
public RotationType RotateTo { set { rotateTo = value; } get { return rotateTo; } }
[SerializeField] private RotationType rotateTo;
/// <summary>Which direction should the hit point rotation be based on?</summary>
public DirectionType NormalDirection { set { normalDirection = value; } get { return normalDirection; } }
[UnityEngine.Serialization.FormerlySerializedAs("normal")][SerializeField] private DirectionType normalDirection = DirectionType.CameraDirection;
/// <summary>Based on the normal direction, what should the rotation be rolled relative to?
/// WorldUp = It will be rolled so the up vector is world up.
/// CameraUp = It will be rolled so the up vector is camera up.
/// DrawAngle = It will be rolled according to the mouse/finger movement on screen.</summary>
public RelativeType NormalRelativeTo { set { normalRelativeTo = value; } get { return normalRelativeTo; } }
[UnityEngine.Serialization.FormerlySerializedAs("orientation")][SerializeField] private RelativeType normalRelativeTo = RelativeType.CameraUp;
/// <summary>This allows you to specify the <b>Transform</b> when using <b>RotateTo = CustomRotation/CustomLocalRotation</b>.</summary>
public Transform CustomTransform { set { customTransform = value; } get { return customTransform; } }
[SerializeField] private Transform customTransform;
/// <summary>Should painting triggered from this component be eligible for being undone?</summary>
public bool StoreStates { set { storeStates = value; } get { return storeStates; } }
[SerializeField] protected bool storeStates = true;
/// <summary>This allows you to override the order this paint gets applied to the object during the current frame.</summary>
public int Priority { set { priority = value; } get { return priority; } }
[SerializeField] private int priority;
/// <summary>If you want the raycast hit point to be offset from the surface a bit, this allows you to set by how much in world space.</summary>
public float NormalOffset { set { normalOffset = value; } get { return normalOffset; } }
[SerializeField] private float normalOffset;
// No longer used
[SerializeField] private ButtonTypes requiredButtons;
// No longer used
[SerializeField] private KeyCode requiredKey;
// No longer used
[SerializeField] private float mouseOffset;
// No longer used
[SerializeField] private float touchOffset;
// No longer used
[SerializeField] private bool showPreview;
private static Transform m_TargetTransform = null;
public static void SetTargetTransform(Transform targetTransform)
{
m_TargetTransform = targetTransform;
}
public bool NeedsDrawAngle
{
get
{
return rotateTo == RotationType.Normal && normalRelativeTo == RelativeType.DrawAngle;
}
}
private bool m_IsAuthorizedDrawing = false;
public void AuthorizedDrawing()
{
m_IsAuthorizedDrawing = true;
}
public void NotAuthorizedDrawing()
{
m_IsAuthorizedDrawing = false;
}
#if UNITY_EDITOR
protected virtual void Reset()
{
if (GetComponent<CwPointerMouse>() == null)
{
requiredButtons &= ~ButtonTypes.LeftMouse;
requiredButtons &= ~ButtonTypes.RightMouse;
requiredButtons &= ~ButtonTypes.MiddleMouse;
gameObject.AddComponent<CwPointerMouse>();
}
if (GetComponent<CwPointerTouch>() == null)
{
requiredButtons &= ~ButtonTypes.Touch;
gameObject.AddComponent<CwPointerTouch>();
}
if (GetComponent<CwPointerPen>() == null)
{
gameObject.AddComponent<CwPointerPen>();
}
}
#endif
public bool ShouldUpgradePointers()
{
return (requiredButtons & ButtonTypes.LeftMouse) != 0
|| (requiredButtons & ButtonTypes.RightMouse) != 0
|| (requiredButtons & ButtonTypes.MiddleMouse) != 0
|| (requiredButtons & ButtonTypes.Touch) != 0;
}
public void TryUpgradePointers()
{
var lmb = (requiredButtons & ButtonTypes.LeftMouse) != 0;
var rmb = (requiredButtons & ButtonTypes.RightMouse) != 0;
var mmb = (requiredButtons & ButtonTypes.MiddleMouse) != 0;
if (lmb == true || rmb == true || mmb == true)
{
requiredButtons &= ~ButtonTypes.LeftMouse;
requiredButtons &= ~ButtonTypes.RightMouse;
requiredButtons &= ~ButtonTypes.MiddleMouse;
var pointerMouse = gameObject.AddComponent<CwPointerMouse>();
pointerMouse.Preview = showPreview;
if (lmb == true)
{
pointerMouse.TryAddKey(KeyCode.Mouse0);
}
if (rmb == true)
{
pointerMouse.TryAddKey(KeyCode.Mouse1);
}
if (mmb == true)
{
pointerMouse.TryAddKey(KeyCode.Mouse2);
}
}
if ((requiredButtons & ButtonTypes.Touch) != 0)
{
requiredButtons &= ~ButtonTypes.Touch;
var pointerTouch = gameObject.AddComponent<CwPointerTouch>();
pointerTouch.Offset = touchOffset;
}
}
protected virtual void DoQuery(Vector2 screenPosition, ref Camera camera, ref Ray ray, ref CwHit hit3D, ref RaycastHit2D hit2D)
{
var hit = default(RaycastHit);
camera = CwHelper.GetCamera(_camera);
ray = camera.ScreenPointToRay(screenPosition);
hit2D = Physics2D.GetRayIntersection(ray, float.PositiveInfinity, layers);
Physics.Raycast(ray, out hit, float.PositiveInfinity, layers);
hit3D = new CwHit(hit);
}
protected void PaintAt(CwPointConnector connector, CwHitCache hitCache, Vector2 screenPosition, Vector2 screenPositionOld, bool preview, float pressure, object owner)
{
var camera = default(Camera);
var ray = default(Ray);
var hit2D = default(RaycastHit2D);
var hit3D = default(CwHit);
var finalPosition = default(Vector3);
var finalRotation = default(Quaternion);
DoQuery(screenPosition, ref camera, ref ray, ref hit3D, ref hit2D);
var valid2D = hit2D.distance > 0.0f;
var valid3D = hit3D.Distance > 0.0f;
// Hit 3D?
if (valid3D == true && (valid2D == false || hit3D.Distance < hit2D.distance))
{
CalcHitData(hit3D.Position, hit3D.Normal, ray, camera, screenPositionOld, ref finalPosition, ref finalRotation);
if (emit == EmitType.PointsIn3D)
{
if (connector != null)
{
if (m_TargetTransform != null && hit3D.Transform != m_TargetTransform) { return; }
if (m_IsAuthorizedDrawing)
{
connector.SubmitPoint(gameObject, preview, priority, pressure, finalPosition, finalRotation, owner);
}
FishingYachtAct.Publish<EventWashingCleanerFollowPositionData>(new EventWashingCleanerFollowPositionData(finalPosition, hit3D.Normal));
}
else
{
hitCache.InvokePoint(gameObject, preview, priority, pressure, finalPosition, finalRotation);
}
return;
}
else if (emit == EmitType.PointsOnUV)
{
hitCache.InvokeCoord(gameObject, preview, priority, pressure, hit3D, finalRotation);
return;
}
else if (emit == EmitType.TrianglesIn3D)
{
hitCache.InvokeTriangle(gameObject, preview, priority, pressure, hit3D, finalRotation);
return;
}
}
// Hit 2D?
else if (valid2D == true)
{
CalcHitData(hit2D.point, new Vector3(0.0f, 0.0f, -1.0f), ray, camera, screenPositionOld, ref finalPosition, ref finalRotation);
if (emit == EmitType.PointsIn3D)
{
if (connector != null)
{
connector.SubmitPoint(gameObject, preview, priority, pressure, finalPosition, finalRotation, owner);
}
else
{
hitCache.InvokePoint(gameObject, preview, priority, pressure, finalPosition, finalRotation);
}
return;
}
}
if (connector != null)
{
connector.BreakHits(owner);
}
}
private void CalcHitData(Vector3 hitPoint, Vector3 hitNormal, Ray ray, Camera camera, Vector2 screenPositionOld, ref Vector3 finalPosition, ref Quaternion finalRotation)
{
finalPosition = hitPoint + hitNormal * normalOffset;
finalRotation = Quaternion.identity;
switch (rotateTo)
{
case RotationType.Normal:
{
var finalNormal = default(Vector3);
switch (normalDirection)
{
case DirectionType.HitNormal: finalNormal = hitNormal; break;
case DirectionType.RayDirection: finalNormal = -ray.direction; break;
case DirectionType.CameraDirection: finalNormal = -camera.transform.forward; break;
}
var finalUp = Vector3.up;
switch (normalRelativeTo)
{
case RelativeType.CameraUp: finalUp = camera.transform.up; break;
case RelativeType.DrawAngle:
{
var rayOld = camera.ScreenPointToRay(screenPositionOld);
if (camera.orthographic == true)
{
finalUp = Vector3.Cross(rayOld.GetPoint(1.0f) - ray.origin, ray.GetPoint(1.0f) - ray.origin);
}
else
{
finalUp = Vector3.Cross(rayOld.direction, ray.direction);
}
}
break;
}
finalRotation = Quaternion.LookRotation(-finalNormal, finalUp);
}
break;
case RotationType.World: finalRotation = Quaternion.identity; break;
case RotationType.ThisRotation: finalRotation = transform.rotation; break;
case RotationType.ThisLocalRotation: finalRotation = transform.localRotation; break;
case RotationType.CustomRotation: if (customTransform != null) finalRotation = customTransform.rotation; break;
case RotationType.CustomLocalRotation: if (customTransform != null) finalRotation = customTransform.localRotation; break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed4ded6cb8412cd49ad4d1b9ed2a5678
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,105 @@
using PaintCore;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace game
{
/// <summary>This component fills the attached UI Image based on the total amount of opaque pixels that have been painted in all active and enabled <b>CwChannelCounter</b> components in the scene.</summary>
[RequireComponent(typeof(Image))]
public class BrushChannelCounterFill : MonoBehaviour
{
public enum ChannelType
{
Red,
Green,
Blue,
Alpha
}
/// <summary>This allows you to specify the counters that will be used.
/// Zero = All active and enabled counters in the scene.</summary>
public List<CwChannelCounter> Counters { get { if (counters == null) counters = new List<CwChannelCounter>(); return counters; } }
[SerializeField] private List<CwChannelCounter> counters;
/// <summary>This allows you to choose which channel will be output to the UI Image.</summary>
public ChannelType Channel { set { channel = value; } get { return channel; } }
[SerializeField] private ChannelType channel;
/// <summary>Inverse the fill?</summary>
public bool Inverse { set { inverse = value; } get { return inverse; } }
[SerializeField] private bool inverse;
private static float m_OmitPercentage = 0.1f;
public static float OmitPercentage { get => m_OmitPercentage; set => m_OmitPercentage = value; }
private bool m_IsOver = false;
[System.NonSerialized]
private Image cachedImage;
private bool m_IsStopping = false;
public void ResetData()
{
m_IsOver = false;
}
public void StopCounting()
{
m_IsStopping = true;
}
public void StartCounting()
{
m_IsStopping = false;
}
protected virtual void OnEnable()
{
cachedImage = GetComponent<Image>();
m_IsOver = false;
}
protected virtual void Update()
{
if (m_IsStopping) return;
var finalCounters = counters.Count > 0 ? counters : null;
var ratio = 0.0f;
switch (channel)
{
case ChannelType.Red: ratio = CwChannelCounter.GetRatioR(finalCounters); break;
case ChannelType.Green: ratio = CwChannelCounter.GetRatioG(finalCounters); break;
case ChannelType.Blue: ratio = CwChannelCounter.GetRatioB(finalCounters); break;
case ChannelType.Alpha: ratio = CwChannelCounter.GetRatioA(finalCounters); break;
}
if (inverse == true)
{
ratio = 1.0f - ratio;
}
FishingYachtAct.Publish<EventWashingMonitoringFoamProgressData>(new EventWashingMonitoringFoamProgressData(ratio));
if (1 - ratio <= BrushChannelCounterFill.m_OmitPercentage)
{
if (m_IsOver == false)
{
FishingYachtAct.Publish<EventWashingConclusionData>(new EventWashingConclusionData());
m_IsOver = true;
}
cachedImage.fillAmount = 1.0f;
}
else
{
float tmp = 1f - BrushChannelCounterFill.m_OmitPercentage;
float realProgress = ratio / tmp;
cachedImage.fillAmount = Mathf.Clamp01(realProgress);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fdfd5b58cbf79143a8b9f55919fe9b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using CW.Common;
using PaintCore;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace game
{
[RequireComponent(typeof(CwHitPointers))]
public class BrushPointerMouse : CwPointer
{
public Vector2 Offset { set { offset = value; } get { return offset; } }
[SerializeField] private Vector2 offset = Vector2.zero;
/// <summary>If you enable this, then a paint preview will be shown under the mouse as long as the <b>RequiredKey</b> is not pressed.</summary>
public bool Preview { set { preview = value; } get { return preview; } }
[SerializeField] private bool preview;
/// <summary>This component will paint while any of the specified mouse buttons or keyboard keys are held.</summary>
public List<KeyCode> Keys { get { if (keys == null) { keys = new List<KeyCode>(); } return keys; } }
[SerializeField] private List<KeyCode> keys;
private readonly int PREVIEW_FINGER_INDEX = -1;
private readonly int PAINT_FINGER_INDEX = 1;
[System.NonSerialized]
private bool oldHeld;
public bool TryAddKey(KeyCode key)
{
if (Keys.Contains(key) == false)
{
keys.Add(key);
return true;
}
return false;
}
public bool GetKeyHeld()
{
if (keys != null)
{
foreach (var key in keys)
{
if (CwInput.GetKeyIsHeld(key) == true)
{
return true;
}
}
}
return false;
}
#if UNITY_EDITOR
protected virtual void Reset()
{
if (keys == null)
{
keys = new List<KeyCode>();
}
else
{
keys.Clear();
}
keys.Add(KeyCode.Mouse0);
}
#endif
protected virtual void Update()
{
var newHeld = false;
var enablePreview = false;
var enablePaint = false;
bool isFollowing = false;
Vector2 screenPosition = Vector2.zero;
if (CwInput.GetMouseExists() == true)
{
CwInputManager.Finger finger;
newHeld = GetKeyHeld();
enablePaint = newHeld == true || oldHeld == true;
enablePreview = preview == true && enablePaint == false;
Vector2 position = CwInput.GetMousePosition();
position += offset * CwInputManager.ScaleFactor;
if (enablePreview == true)
{
GetFinger(PREVIEW_FINGER_INDEX, position, 1.0f, true, out finger);
cachedHitPointers.HandleFingerUpdate(finger, false, false);
screenPosition = position;
isFollowing = true;
}
if (enablePaint == true)
{
if (EventSystem.current == null) return;
if (EventSystem.current.IsPointerOverGameObject()) return;
var down = GetFinger(PAINT_FINGER_INDEX, position, 1.0f, true, out finger);
cachedHitPointers.HandleFingerUpdate(finger, down, newHeld == false);
screenPosition = position;
isFollowing = true;
}
}
if (isFollowing) FishingYachtAct.Publish<EventWashingGestureFollowPositionData>(new EventWashingGestureFollowPositionData(screenPosition, true));
else FishingYachtAct.Publish<EventWashingGestureFollowPositionData>(new EventWashingGestureFollowPositionData(screenPosition, false));
if (enablePreview == false)
{
TryNullFinger(PREVIEW_FINGER_INDEX);
}
if (enablePaint == false)
{
TryNullFinger(PAINT_FINGER_INDEX);
}
oldHeld = newHeld;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a76738f841592a4fb2fded5869f4534
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using CW.Common;
using PaintCore;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace game
{
[RequireComponent(typeof(CwHitPointers))]
public class BrushPointerTouch : CwPointer
{
/// <summary>If you want the paint to appear above the finger, then you can set this number to something positive.</summary>
public Vector2 Offset { set { offset = value; } get { return offset; } }
[SerializeField] private Vector2 offset = Vector2.zero;
protected virtual void Update()
{
CwInputManager.Finger finger;
bool isFollowing = false;
Vector2 screenPosition = Vector2.zero;
for (var i = 0; i < CwInput.GetTouchCount(); i++)
{
int index;
Vector2 position;
float pressure;
bool set;
if (EventSystem.current == null) return;
if (EventSystem.current.IsPointerOverGameObject()) return;
CwInput.GetTouch(i, out index, out position, out pressure, out set);
position += offset * CwInputManager.ScaleFactor;
var down = GetFinger(index, position, pressure, set, out finger);
cachedHitPointers.HandleFingerUpdate(finger, down, set == false);
screenPosition = position;
isFollowing = true;
if (set == false)
{
TryNullFinger(index);
}
}
if (isFollowing) FishingYachtAct.Publish<EventWashingGestureFollowPositionData>(new EventWashingGestureFollowPositionData(screenPosition, true));
else FishingYachtAct.Publish<EventWashingGestureFollowPositionData>(new EventWashingGestureFollowPositionData(screenPosition, false));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb3ec75e2f551f248a6e890b43a4be89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,358 @@
using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace game
{
[ExecuteAlways]
[DisallowMultipleComponent]
public class CameraFollowPathMoveController : MonoBehaviour
{
/// <summary>The path to follow</summary>
[Tooltip("The path to follow")]
public CinemachinePath Path;
[Tooltip("路径停歇点索引,循环执行。eg:5个点执行到最后一个位置会从移动到初始位置开始。")]
public List<int> m_RestIndexList = new List<int>();
[Tooltip("磁性距离,距离目标点一定距离,即认为到达目标点。")]
public float m_MagneticDistance = 0.01f;
[Tooltip("一直移动,可以查看整个移动过程,主要用于本地测试。发包时关闭。")]
public bool IsAlwaysMoving = false;
[Tooltip("是否开启测试停歇点配合TestRestIndex使用主要用于本地测试。发包时关闭。")]
public bool m_IsTestRest = false;
[Tooltip("测试用停歇点索引配合IsTestRest使用默认为-1,在0-停歇点索引位之间生效。")]
public int m_TestRestIndex = -1;
/// <summary>This enum defines the options available for the update method.</summary>
public enum UpdateMethod
{
/// <summary>Updated in normal MonoBehaviour Update.</summary>
Update,
/// <summary>Updated in sync with the Physics module, in FixedUpdate</summary>
FixedUpdate,
/// <summary>Updated in normal MonoBehaviour LateUpdate</summary>
LateUpdate
};
/// <summary>When to move the cart, if Velocity is non-zero</summary>
[Tooltip("When to move the cart, if Velocity is non-zero")]
public UpdateMethod m_UpdateMethod = UpdateMethod.Update;
/// <summary>How to interpret the Path Position</summary>
[Tooltip("How to interpret the Path Position. If set to Path Units, values are as follows: 0 represents the first waypoint on the path, 1 is the second, and so on. Values in-between are points on the path in between the waypoints. If set to Distance, then Path Position represents distance along the path.")]
[HideInInspector] public CinemachinePathBase.PositionUnits m_PositionUnits = CinemachinePathBase.PositionUnits.Distance;
/// <summary>Move the cart with this speed</summary>
[Tooltip("Move the cart with this speed along the path. The value is interpreted according to the Position Units setting.")]
[FormerlySerializedAs("m_Velocity")]
public float Speed;
/// <summary>The cart's current position on the path, in distance units</summary>
[Tooltip("The position along the path at which the cart will be placed. This can be animated directly or, if the velocity is non-zero, will be updated automatically. The value is interpreted according to the Position Units setting.")]
[FormerlySerializedAs("m_CurrentDistance")]
[HideInInspector] public float m_Position;
private enum MoveState
{
Stationary,
Moving
}
private MoveState m_State = MoveState.Stationary;
private int m_MoveIndex = 0;
private int m_CurPathIndex = 0;
//private int m_MoveDirection = 0;
void Awake()
{
#if UNITY_EDITOR
if (Path != null)
{
ResamplePath();
JumpToRestIndex(0);
SetCartPosition(0f);
}
#endif
}
void FixedUpdate()
{
if (m_State == MoveState.Stationary && !IsAlwaysMoving) return;
if (m_UpdateMethod == UpdateMethod.FixedUpdate)
SetCartPosition(m_Position + Speed * Time.deltaTime);
}
void Update()
{
CheckTestRest();
if (m_State == MoveState.Stationary && !IsAlwaysMoving) return;
float speed = Application.isPlaying ? Speed : 0;
if (m_UpdateMethod == UpdateMethod.Update)
SetCartPosition(m_Position + speed * Time.deltaTime);
}
void LateUpdate()
{
if (m_State == MoveState.Stationary && !IsAlwaysMoving) return;
if (!Application.isPlaying)
SetCartPosition(m_Position);
else if (m_UpdateMethod == UpdateMethod.LateUpdate)
SetCartPosition(m_Position + Speed * Time.deltaTime);
}
public void ResetAndInit(CinemachinePath followPath, List<int> restIndexList, float magneticDistance)
{
if (followPath == null || restIndexList == null) return;
if (Path)
{
Path = null;
}
if (m_RestIndexList != null)
{
m_RestIndexList = null;
}
m_MagneticDistance = magneticDistance;
Path = followPath;
m_RestIndexList = restIndexList;
ResamplePath();
}
void SetCartPosition(float distanceAlongPath)
{
if (Path != null)
{
m_Position = Path.StandardizeUnit(distanceAlongPath, m_PositionUnits);
if (!IsAlwaysMoving) m_Position = CheckPosition(distanceAlongPath, m_Position);
transform.position = Path.EvaluatePositionAtUnit(m_Position, m_PositionUnits);
transform.rotation = Path.EvaluateOrientationAtUnit(m_Position, m_PositionUnits);
}
}
private void CheckTestRest()
{
if (m_IsTestRest)
{
if (m_TestRestIndex >= 0 && m_TestRestIndex < m_RestIndexList.Count)
{
m_MoveIndex = m_TestRestIndex;
if (!IsEffective()) return;
GoToPositionIndex(m_RestIndexList[m_MoveIndex]);
}
}
}
List<Vector3[]> m_RestPointDistance = new List<Vector3[]>();
private void ResamplePath()
{
int stepsPerSegment = Path.DistanceCacheSampleStepsPerSegment;
float minPos = Path.MinPos;
float maxPos = Path.MaxPos;
float stepSize = 1f / Mathf.Max(1, stepsPerSegment);
// Sample the positions
int numKeys = Mathf.RoundToInt((maxPos - minPos) / stepSize) + 1;
float[] m_PosToDistance = new float[numKeys];
m_RestPointDistance.Clear();
Vector3[] firstRestDistance = new Vector3[2];
Vector3 firstDest = new Vector3(-m_MagneticDistance, 0f, m_MagneticDistance);
firstRestDistance[0] = firstDest;
m_RestPointDistance.Add(firstRestDistance);
int index = 0;
float m_PathLength = 0f;
Vector3 p0 = Path.EvaluatePosition(0);
m_PosToDistance[0] = 0;
float pos = minPos;
for (int i = 1; i < numKeys; ++i)
{
pos += stepSize;
Vector3 p = Path.EvaluatePosition(pos);
float d = Vector3.Distance(p0, p);
m_PathLength += d;
p0 = p;
m_PosToDistance[i] = m_PathLength;
if (i % stepsPerSegment == 0)
{
Vector3[] tmpArr = new Vector3[1];
Vector3 tmp = new Vector3(m_PathLength - m_MagneticDistance, m_PathLength, m_PathLength + m_MagneticDistance);
tmpArr[0] = tmp;
m_RestPointDistance.Add(tmpArr);
++index;
}
}
if (Path.Looped)
m_RestPointDistance.RemoveAt(index);
Vector3 lastDest = new Vector3(m_PathLength - m_MagneticDistance, m_PathLength, m_PathLength + m_MagneticDistance);
firstRestDistance[1] = lastDest;
}
private bool IsEffective()
{
return (m_MoveIndex >= 0 && m_MoveIndex < m_RestIndexList.Count && m_RestIndexList.Count > 0) ? true : false;
}
public int RestCount
{
get
{
if (m_RestIndexList == null) return 0;
return m_RestIndexList.Count;
}
}
public int MoveIndex
{
get => m_MoveIndex;
}
public int ForwardToNextRest()
{
m_MoveIndex++;
if (m_MoveIndex > m_RestIndexList.Count - 1)
{
if (Path.Looped)
m_MoveIndex = 0;
else
m_MoveIndex = m_RestIndexList.Count - 1;
}
GoToPositionIndex(m_RestIndexList[m_MoveIndex]);
if (Speed < 0)
{
Speed = -Speed;
}
return m_MoveIndex;
}
public int BackwardToNextRest()
{
m_MoveIndex--;
if (m_MoveIndex < 0)
{
if (Path.Looped)
m_MoveIndex = m_RestIndexList.Count - 1;
else
m_MoveIndex = 0;
}
GoToPositionIndex(m_RestIndexList[m_MoveIndex]);
if (Speed > 0)
{
Speed = -Speed;
}
return m_MoveIndex;
}
public int JumpToNextRestForward()
{
JumpToRestIndex(++m_MoveIndex);
return m_MoveIndex;
}
public int JumpToNextRestBackward()
{
JumpToRestIndex(--m_MoveIndex);
return m_MoveIndex;
}
public int JumpToRestIndex(int index)
{
m_MoveIndex = index;
if (m_MoveIndex < 0)
{
if (Path.Looped)
m_MoveIndex = m_RestIndexList.Count - 1;
else
m_MoveIndex = 0;
}
else if (m_MoveIndex > m_RestIndexList.Count - 1)
{
if (Path.Looped)
m_MoveIndex = 0;
else
m_MoveIndex = m_RestIndexList.Count - 1;
}
GoToPositionIndex(m_RestIndexList[m_MoveIndex]);
var tmpArr = m_RestPointDistance[m_CurPathIndex];
Vector3 boundary = tmpArr[0];
float distanceAlongPath = boundary.y;
m_Position = Path.StandardizeUnit(distanceAlongPath, m_PositionUnits);
SetCartPosition(m_Position);
m_State = MoveState.Stationary;
return m_MoveIndex;
}
public void GoToPositionIndex(int index)
{
m_CurPathIndex = index;
m_State = MoveState.Moving;
}
float CheckPosition(float distanceAlongPath, float distance)
{
if (m_CurPathIndex < 0 || m_CurPathIndex >= m_RestPointDistance.Count) return distance;
var tmpArr = m_RestPointDistance[m_CurPathIndex];
int count = tmpArr.Length;
if (count == 0) return distance;
if (m_CurPathIndex == 0)
{
if (Speed > 0f)
{
if (distanceAlongPath >= Path.PathLength)
{
m_State = MoveState.Stationary;
m_IsTestRest = false;
FishingYachtAct.Publish<EventWashingCameraStopData>(new EventWashingCameraStopData());
return tmpArr[0].y;
}
}
else if (Speed < 0f)
{
if (distanceAlongPath <= 0f)
{
m_State = MoveState.Stationary;
m_IsTestRest = false;
FishingYachtAct.Publish<EventWashingCameraStopData>(new EventWashingCameraStopData());
return tmpArr[0].y;
}
}
}
else
{
if (Speed > 0f)
{
if (distance >= tmpArr[0].x)
{
m_State = MoveState.Stationary;
m_IsTestRest = false;
FishingYachtAct.Publish<EventWashingCameraStopData>(new EventWashingCameraStopData());
return tmpArr[0].y;
}
}
else if (Speed <= 0f)
{
if (distance <= tmpArr[0].z)
{
m_State = MoveState.Stationary;
m_IsTestRest = false;
FishingYachtAct.Publish<EventWashingCameraStopData>(new EventWashingCameraStopData());
return tmpArr[0].y;
}
}
}
return distance;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 425c3dabfac452a4e9fd1c91aa086545
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,241 @@
using cfg;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace game
{
public class OperatingToolsBase : MonoBehaviour
{
[Tooltip("工具ID")]
public int m_ID = 0;
[Tooltip("工具操作前的准备时间,常用于移到指定位置等。")]
public float m_PreparationTime = 0.1f;
//[Tooltip("初始Transform")]
[HideInInspector] public Transform InitTransfom;
#region Transform变换相关
/// <summary>
/// 跳转到展示区(即初始Transform : m_InitTransfom)
/// </summary>
public void JumpToExhibition()
{
if (InitTransfom is null) return;
this.transform.parent = InitTransfom.parent;
this.transform.SetParent(InitTransfom.parent, false);
this.transform.localScale = InitTransfom.localScale;
this.transform.rotation = InitTransfom.rotation;
this.transform.position = InitTransfom.position;
}
/// <summary>
/// 移动到指定 Transform
/// </summary>
/// <param name="transform"></param>
public void MoveToTransform(Transform transform)
{
if (transform is null) return;
this.transform.DOKill();
this.transform.DOMove(transform.position, m_PreparationTime).SetEase(Ease.OutQuad);
this.transform.DOScale(transform.localScale, m_PreparationTime).SetEase(Ease.Linear);
this.transform.DORotateQuaternion(transform.rotation, m_PreparationTime).SetEase(Ease.Linear);
}
/// <summary>
/// 返回到展示区(即初始Transform : m_InitTransfom)
/// </summary>
public virtual void MoveToExhibition()
{
// 停止播放操作动画
StopPlayOperationAnimation();
// 停止播放准备动画
StopPlayPrepareAnimation();
if (InitTransfom == null) return;
MoveToTransform(InitTransfom);
m_Status = EventWashingActionStatus.Invalid;
}
#endregion
#region
protected bool m_IsAuthorizedAnimation = true;
public bool IsAuthorizedAnimation { get => m_IsAuthorizedAnimation; }
public void AuthorizedAnimation()
{
m_IsAuthorizedAnimation = true;
}
public void NotAuthorizedAnimation()
{
m_IsAuthorizedAnimation = false;
}
/// <summary>
/// 播放工具操作动画
/// </summary>
protected virtual void PlayOperationAnimation()
{
}
/// <summary>
/// 停止播放工具操作动画
/// </summary>
protected virtual void StopPlayOperationAnimation()
{
}
/// <summary>
/// 播放准备动画
/// </summary>
protected virtual void PlayPrepareAnimation()
{
}
/// <summary>
/// 停止播放准备动画
/// </summary>
protected virtual void StopPlayPrepareAnimation()
{
}
#endregion
#region
/// <summary>
/// 开始工具操作行为
/// </summary>
/// <param name="overAction"></param>
public virtual void StartExecuting()
{
m_Status = EventWashingActionStatus.Executing;
m_PrepareCumulativeTime = 0f;
// 播放操作动画
if (m_IsAuthorizedAnimation) PlayOperationAnimation();
}
/// <summary>
/// 操作前的准备行为,如:移动等
/// </summary>
/// <param name="args"></param>
public void PrepareAction(Transform transform, UnityAction<OperatingToolsBase> readyAction, params object[] args)
{
m_Status = EventWashingActionStatus.Preparing;
m_PrepareCumulativeTime = 0f;
m_ReadyAction = readyAction;
MoveToTransform(transform);
if (m_IsAuthorizedAnimation) PlayPrepareAnimation();
}
/// <summary>
/// 停止操作行为
/// </summary>
public virtual void StopExecuting()
{
if (m_Status == EventWashingActionStatus.Executing)
{
MoveToExhibition();
StopPlayOperationAnimation();
}
m_Status = EventWashingActionStatus.Stopping;
}
public virtual void Enter1stStage()
{
}
public virtual void Exit1stStage()
{
}
public virtual void Enter2ndStage()
{
}
public virtual void Exit2ndStage()
{
}
#endregion
#region
/// <summary>
/// 准备结束事件
/// </summary>
UnityAction<OperatingToolsBase> m_ReadyAction = null;
#endregion
#region &
/// <summary>
/// 状态
/// </summary>
protected EventWashingActionStatus m_Status = EventWashingActionStatus.Invalid;
public EventWashingActionStatus Status
{
get { return m_Status; }
}
/// <summary>
/// 准备操作累计时间,用以本地计时
/// </summary>
protected float m_PrepareCumulativeTime = 0f;
protected EventWashingTool m_Type = EventWashingTool.None; // 工具类型
public EventWashingTool ToolType { get { return m_Type; } }
public int ToolID { get { return m_ID; } }
#endregion
#region
/// <summary>
/// 初始化工具
/// </summary>
public virtual void InitTool()
{
m_Type = EventWashingTool.None;
m_PrepareCumulativeTime = 0f;
m_Status = EventWashingActionStatus.Invalid;
JumpToExhibition();
this.gameObject.SetActive(false);
}
// Start is called before the first frame update
protected virtual void Start()
{
m_PrepareCumulativeTime = 0f;
m_Status = EventWashingActionStatus.Invalid;
}
// Update is called once per frame
protected virtual void Update()
{
if (m_Status == EventWashingActionStatus.Preparing)
{
m_PrepareCumulativeTime += Time.deltaTime;
if (m_PrepareCumulativeTime >= m_PreparationTime)
{
if (m_ReadyAction != null)
{
m_ReadyAction.Invoke(this);
}
m_PrepareCumulativeTime = 0f;
m_Status = EventWashingActionStatus.Executing;
StopPlayPrepareAnimation();
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a33d07ea5bd0aa94582860610152f412
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,309 @@
//
// Outline.cs
// QuickOutline
//
// Created by Chris Nolet on 3/30/18.
// Copyright © 2018 Chris Nolet. All rights reserved.
//
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
[DisallowMultipleComponent]
public class Outline : MonoBehaviour {
private static HashSet<Mesh> registeredMeshes = new HashSet<Mesh>();
public enum Mode {
OutlineAll,
OutlineVisible,
OutlineHidden,
OutlineAndSilhouette,
SilhouetteOnly
}
public Mode OutlineMode {
get { return outlineMode; }
set {
outlineMode = value;
needsUpdate = true;
}
}
public Color OutlineColor {
get { return outlineColor; }
set {
outlineColor = value;
needsUpdate = true;
}
}
public float OutlineWidth {
get { return outlineWidth; }
set {
outlineWidth = value;
needsUpdate = true;
}
}
[Serializable]
private class ListVector3 {
public List<Vector3> data;
}
[SerializeField]
private Mode outlineMode;
[SerializeField]
private Color outlineColor = Color.white;
[SerializeField, Range(0f, 10f)]
private float outlineWidth = 2f;
[Header("Optional")]
[SerializeField, Tooltip("Precompute enabled: Per-vertex calculations are performed in the editor and serialized with the object. "
+ "Precompute disabled: Per-vertex calculations are performed at runtime in Awake(). This may cause a pause for large meshes.")]
private bool precomputeOutline;
[SerializeField, HideInInspector]
private List<Mesh> bakeKeys = new List<Mesh>();
[SerializeField, HideInInspector]
private List<ListVector3> bakeValues = new List<ListVector3>();
private Renderer[] renderers;
private Material outlineMaskMaterial;
private Material outlineFillMaterial;
private bool needsUpdate;
void Awake() {
// Cache renderers
renderers = GetComponentsInChildren<Renderer>();
// Instantiate outline materials
outlineMaskMaterial = Instantiate(Resources.Load<Material>(@"Materials/OutlineMask"));
outlineFillMaterial = Instantiate(Resources.Load<Material>(@"Materials/OutlineFill"));
outlineMaskMaterial.name = "OutlineMask (Instance)";
outlineFillMaterial.name = "OutlineFill (Instance)";
// Retrieve or generate smooth normals
LoadSmoothNormals();
// Apply material properties immediately
needsUpdate = true;
}
void OnEnable() {
foreach (var renderer in renderers) {
// Append outline shaders
var materials = renderer.sharedMaterials.ToList();
materials.Add(outlineMaskMaterial);
materials.Add(outlineFillMaterial);
renderer.materials = materials.ToArray();
}
}
void OnValidate() {
// Update material properties
needsUpdate = true;
// Clear cache when baking is disabled or corrupted
if (!precomputeOutline && bakeKeys.Count != 0 || bakeKeys.Count != bakeValues.Count) {
bakeKeys.Clear();
bakeValues.Clear();
}
// Generate smooth normals when baking is enabled
if (precomputeOutline && bakeKeys.Count == 0) {
Bake();
}
}
void Update() {
if (needsUpdate) {
needsUpdate = false;
UpdateMaterialProperties();
}
}
void OnDisable() {
foreach (var renderer in renderers) {
// Remove outline shaders
var materials = renderer.sharedMaterials.ToList();
materials.Remove(outlineMaskMaterial);
materials.Remove(outlineFillMaterial);
renderer.materials = materials.ToArray();
}
}
void OnDestroy() {
// Destroy material instances
Destroy(outlineMaskMaterial);
Destroy(outlineFillMaterial);
}
void Bake() {
// Generate smooth normals for each mesh
var bakedMeshes = new HashSet<Mesh>();
foreach (var meshFilter in GetComponentsInChildren<MeshFilter>()) {
// Skip duplicates
if (!bakedMeshes.Add(meshFilter.sharedMesh)) {
continue;
}
// Serialize smooth normals
var smoothNormals = SmoothNormals(meshFilter.sharedMesh);
bakeKeys.Add(meshFilter.sharedMesh);
bakeValues.Add(new ListVector3() { data = smoothNormals });
}
}
void LoadSmoothNormals() {
// Retrieve or generate smooth normals
foreach (var meshFilter in GetComponentsInChildren<MeshFilter>()) {
// Skip if smooth normals have already been adopted
if (!registeredMeshes.Add(meshFilter.sharedMesh)) {
continue;
}
// Retrieve or generate smooth normals
var index = bakeKeys.IndexOf(meshFilter.sharedMesh);
var smoothNormals = (index >= 0) ? bakeValues[index].data : SmoothNormals(meshFilter.sharedMesh);
// Store smooth normals in UV3
meshFilter.sharedMesh.SetUVs(3, smoothNormals);
// Combine submeshes
var renderer = meshFilter.GetComponent<Renderer>();
if (renderer != null) {
CombineSubmeshes(meshFilter.sharedMesh, renderer.sharedMaterials);
}
}
// Clear UV3 on skinned mesh renderers
foreach (var skinnedMeshRenderer in GetComponentsInChildren<SkinnedMeshRenderer>()) {
// Skip if UV3 has already been reset
if (!registeredMeshes.Add(skinnedMeshRenderer.sharedMesh)) {
continue;
}
// Clear UV3
skinnedMeshRenderer.sharedMesh.uv4 = new Vector2[skinnedMeshRenderer.sharedMesh.vertexCount];
// Combine submeshes
CombineSubmeshes(skinnedMeshRenderer.sharedMesh, skinnedMeshRenderer.sharedMaterials);
}
}
List<Vector3> SmoothNormals(Mesh mesh) {
// Group vertices by location
var groups = mesh.vertices.Select((vertex, index) => new KeyValuePair<Vector3, int>(vertex, index)).GroupBy(pair => pair.Key);
// Copy normals to a new list
var smoothNormals = new List<Vector3>(mesh.normals);
// Average normals for grouped vertices
foreach (var group in groups) {
// Skip single vertices
if (group.Count() == 1) {
continue;
}
// Calculate the average normal
var smoothNormal = Vector3.zero;
foreach (var pair in group) {
smoothNormal += smoothNormals[pair.Value];
}
smoothNormal.Normalize();
// Assign smooth normal to each vertex
foreach (var pair in group) {
smoothNormals[pair.Value] = smoothNormal;
}
}
return smoothNormals;
}
void CombineSubmeshes(Mesh mesh, Material[] materials) {
// Skip meshes with a single submesh
if (mesh.subMeshCount == 1) {
return;
}
// Skip if submesh count exceeds material count
if (mesh.subMeshCount > materials.Length) {
return;
}
// Append combined submesh
mesh.subMeshCount++;
mesh.SetTriangles(mesh.triangles, mesh.subMeshCount - 1);
}
void UpdateMaterialProperties() {
// Apply properties according to mode
outlineFillMaterial.SetColor("_OutlineColor", outlineColor);
switch (outlineMode) {
case Mode.OutlineAll:
outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
break;
case Mode.OutlineVisible:
outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
break;
case Mode.OutlineHidden:
outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Greater);
outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
break;
case Mode.OutlineAndSilhouette:
outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Always);
outlineFillMaterial.SetFloat("_OutlineWidth", outlineWidth);
break;
case Mode.SilhouetteOnly:
outlineMaskMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.LessEqual);
outlineFillMaterial.SetFloat("_ZTest", (float)UnityEngine.Rendering.CompareFunction.Greater);
outlineFillMaterial.SetFloat("_OutlineWidth", 0f);
break;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7227527bced7eb64a8daf3095fd332d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using cfg;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolBubbleGun : ToolForResponsiveCleaner
{
#region
public override void InitTool()
{
base.InitTool();
m_Type = EventWashingTool.FoamSprayer;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 26a5ca7c43397254f8a5cdd4566291b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolCollection : MonoBehaviour
{
public List<OperatingToolsBase> Tools = new List<OperatingToolsBase>();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0af65a08f04dedc4ab200eaff1bbc959
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolForFollowerAndNormalPosition : OperatingToolsBase
{
[Tooltip("工具距离操作点位置, 吸尘器/水枪等 本身到吸嘴距离")]
[SerializeField]
private float m_DistanceToMouth = 0.1f;
[Tooltip("工具操作点距离操作区距离, 吸尘器/水枪等 吸嘴离地距离")]
[SerializeField]
private float m_DistanceFromMouthToGround = 0.4f;
[Tooltip("作用距离/覆盖范围")]
[SerializeField]
private float m_RangeCoverage = 0.8f;
[Tooltip("工具移动速度")]
[SerializeField]
private float m_ToolSpeed = 10f;
[Tooltip("工具移动耗时调整,按比例计算,正值为增加,负值为减少")]
[SerializeField]
private float m_ToolConsumedTimeAdjustedRatio = 0f;
public float DistanceFromMouthToGround { get => m_DistanceFromMouthToGround; }
public float RangeCoverage { get => m_RangeCoverage; }
private Vector3 m_OperatingPoint = Vector3.zero;
public Vector3 OperatingPoint { get => m_OperatingPoint; }
public void Follow(Vector3 position, Vector3 normal)
{
Vector3 targetPosition = position + normal * (m_DistanceToMouth + m_DistanceFromMouthToGround);
float distance = Vector3.Distance(targetPosition, this.transform.position);
float time = distance / m_ToolSpeed;
time += time * m_ToolConsumedTimeAdjustedRatio;
m_OperatingPoint = position + normal * m_DistanceFromMouthToGround;
this.transform.DOKill();
this.transform.DOMove(targetPosition, time).SetEase(Ease.OutQuad);
this.transform.DORotateQuaternion(Quaternion.LookRotation(-normal), time).SetEase(Ease.Linear);
}
#region Transform变换相关
public override void MoveToExhibition()
{
// 停止播放操作动画
StopPlayOperationAnimation();
// 停止播放准备动画
StopPlayPrepareAnimation();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b990b7047f8703f4d92bfbaee7dc7143
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolForResponsiveCleaner : ToolForFollowerAndNormalPosition
{
[Tooltip("喷涂动画")]
[SerializeField]
private SprayAnimation m_SprayAnimation = null;
#region
protected override void PlayOperationAnimation()
{
base.PlayOperationAnimation();
if (m_SprayAnimation != null ) { m_SprayAnimation.PlayEffect(); }
}
protected override void StopPlayOperationAnimation()
{
base.StopPlayOperationAnimation();
if (m_SprayAnimation != null) { m_SprayAnimation.StopEffect(); }
}
#endregion
#region
public override void InitTool()
{
base.InitTool();
m_SprayAnimation.StopEffect();
m_SprayAnimation.DisableEffect();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36d513f4aabe5d742b1d06e036c77af4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,124 @@
using cfg;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using UnityEngine;
namespace game
{
public class ToolShovel : OperatingToolsBase
{
[Tooltip("动画")]
[SerializeField]
private Animation m_Animation = null;
[Tooltip("循环动画")]
[SerializeField]
private string m_LoopAnimation = null;
[Tooltip("最后一击动画")]
[SerializeField]
private string m_FinalBlowAnimation = null;
[Tooltip("循环动画声音节点")]
[SerializeField]
private GameObject m_LoopAnimationNode = null;
[Tooltip("最后一击动画声音节点")]
[SerializeField]
private GameObject m_FinalBlowAnimationNode = null;
#region
public override void Enter1stStage()
{
base.Enter1stStage();
if (m_Animation != null)
{
m_Animation.Play(m_LoopAnimation);
}
if (m_LoopAnimationNode != null && m_LoopAnimationNode.activeSelf == false)
{
m_LoopAnimationNode.SetActive(true);
}
}
public override void Exit1stStage()
{
base.Exit1stStage();
if (m_Animation != null)
{
m_Animation.Stop(m_LoopAnimation);
m_Animation.transform.localPosition = Vector3.zero;
m_Animation.transform.localEulerAngles = Vector3.zero;
}
if (m_LoopAnimationNode != null && m_LoopAnimationNode.activeSelf == true)
{
m_LoopAnimationNode.SetActive(false);
}
}
public override void Enter2ndStage()
{
base.Enter2ndStage();
if (m_Animation != null)
{
m_Animation.Play(m_FinalBlowAnimation);
}
if (m_FinalBlowAnimationNode != null && m_FinalBlowAnimationNode.activeSelf == false)
{
m_FinalBlowAnimationNode.SetActive(true);
}
}
public override void Exit2ndStage()
{
base.Exit2ndStage();
if (m_Animation != null)
{
m_Animation.transform.localPosition = Vector3.zero;
m_Animation.transform.localEulerAngles = Vector3.zero;
}
if (m_FinalBlowAnimationNode != null && m_FinalBlowAnimationNode.activeSelf == true)
{
m_FinalBlowAnimationNode.SetActive(false);
}
}
private void StopAllAnimation()
{
if (m_Animation != null)
{
m_Animation.Stop();
m_Animation.transform.localPosition = Vector3.zero;
m_Animation.transform.localEulerAngles = Vector3.zero;
}
if (m_LoopAnimationNode != null && m_LoopAnimationNode.activeSelf == true)
{
m_LoopAnimationNode.SetActive(false);
}
if (m_FinalBlowAnimationNode != null && m_FinalBlowAnimationNode.activeSelf == true)
{
m_FinalBlowAnimationNode.SetActive(false);
}
}
public override void StopExecuting()
{
base.StopExecuting();
StopAllAnimation();
}
#endregion
#region
public override void InitTool()
{
base.InitTool();
m_Type = EventWashingTool.Shovel;
m_PrepareCumulativeTime = 0f;
StopAllAnimation();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d49af091646d384796cea1ce36df9ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
using cfg;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolSpanner : OperatingToolsBase
{
[Tooltip("动画")]
[SerializeField]
private Animation m_Animation = null;
[Tooltip("循环动画")]
[SerializeField]
private string m_LoopAnimation = null;
[Tooltip("循环动画声音节点")]
[SerializeField]
private GameObject m_LoopAnimationNode = null;
#region
protected override void PlayOperationAnimation()
{
base.PlayOperationAnimation();
if (m_Animation != null)
{
m_Animation.Play(m_LoopAnimation);
m_IsAuthorizedAnimation = true;
}
if (m_LoopAnimationNode != null && m_LoopAnimationNode.activeSelf == false)
{
m_LoopAnimationNode.SetActive(true);
}
}
protected override void StopPlayOperationAnimation()
{
base.StopPlayOperationAnimation();
StopAllAnimation();
}
private void StopAllAnimation()
{
if (m_Animation != null)
{
m_Animation.Stop();
m_Animation.transform.localPosition = Vector3.zero;
m_Animation.transform.localEulerAngles = Vector3.zero;
}
if (m_LoopAnimationNode != null && m_LoopAnimationNode.activeSelf == true)
{
m_LoopAnimationNode.SetActive(false);
}
}
public override void StopExecuting()
{
base.StopExecuting();
StopAllAnimation();
}
#endregion
#region
public override void InitTool()
{
base.InitTool();
m_Type = EventWashingTool.Spanner;
m_PrepareCumulativeTime = 0f;
StopAllAnimation();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f07ef0072122b9242a239e3d9e35bf13
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using cfg;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolVacuumCleaner : ToolForFollowerAndNormalPosition
{
[Tooltip("循环特效")]
[SerializeField]
private ParticleSystem[] m_LoopEffects = null;
#region
private bool m_IsPlayingEffect = false;
public override void StartExecuting()
{
if (m_LoopEffects == null || m_IsPlayingEffect == true) return;
m_IsPlayingEffect = true;
int count = m_LoopEffects.Length;
for (int i = 0; i < count; i++)
{
m_LoopEffects[i].gameObject.SetActive(true);
m_LoopEffects[i].Play();
}
}
public override void StopExecuting()
{
if (m_LoopEffects == null) return;
m_IsPlayingEffect = false;
int count = m_LoopEffects.Length;
for (int i = 0; i < count; i++)
{
m_LoopEffects[i].Stop();
m_LoopEffects[i].gameObject.SetActive(false);
}
}
#endregion
#region
public override void InitTool()
{
base.InitTool();
m_Type = EventWashingTool.Cleaner;
m_IsPlayingEffect = false;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e9f93b21c65941e4ea6319d8ccbc287f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
using cfg;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace game
{
public class ToolWaterGun : ToolForResponsiveCleaner
{
#region
public override void InitTool()
{
base.InitTool();
m_Type = EventWashingTool.WaterSprayer;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 756bbf0cc789237458db69c086373380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: