Skip to content

Commit

Permalink
* tablet photo events
Browse files Browse the repository at this point in the history
* starting on penguin awareness
  • Loading branch information
BeauPrime committed Dec 19, 2024
1 parent 7256cad commit 073fed1
Show file tree
Hide file tree
Showing 23 changed files with 432 additions and 106 deletions.
6 changes: 5 additions & 1 deletion Assets/Code/Consts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ static public class GameTriggers {
static public readonly StringHash32 AtWarpPoint = "AtWarpPoint";
static public readonly StringHash32 PlayerLookAtObject = "PlayerLookAt";

static public readonly StringHash32 TabletLookAtObject = "TabletLookAt";
static public readonly StringHash32 TabletLookAtObject = "TabletLookAt";

static public readonly StringHash32 TabletPhotoTaken = "TabletPhotoTaken";
static public readonly StringHash32 TabletNewBehaviorCaptured = "TabletBehaviorCaptured";
static public readonly StringHash32 TabletNewBehaviorInstanceCaptured = "TabletBehaviorInstanceCaptured";
}
}
6 changes: 6 additions & 0 deletions Assets/Code/GameLayers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ static public class LayerMasks {
// Layer 20: ParticleAvoid
public const int ParticleAvoid_Index = 20;
public const int ParticleAvoid_Mask = 1048576;
// Layer 21: CaptureOnly
public const int CaptureOnly_Index = 21;
public const int CaptureOnly_Mask = 2097152;
// Layer 22: PenguinSensor
public const int PenguinSensor_Index = 22;
public const int PenguinSensor_Mask = 4194304;
// Layer 29: VisibleOnlyThroughTablet
public const int VisibleOnlyThroughTablet_Index = 29;
public const int VisibleOnlyThroughTablet_Mask = 536870912;
Expand Down
69 changes: 68 additions & 1 deletion Assets/Code/Penguin/PenguinContacts.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using BeauUtil;
using FieldDay.Components;
using FieldDay.Physics;
using FieldDay.Scripting;
using FieldDay.VRHands;
using UnityEngine;
Expand All @@ -10,24 +11,33 @@ namespace Pennycook {
public class PenguinContacts : ScriptActorComponent {
static public readonly StringHash32 Signal_PlayerGripped = "penguin-gripped";
static public readonly StringHash32 Signal_PlayerReleased = "penguin-released";
static public readonly StringHash32 Signal_PlayerEnteredProximity = "penguin-player-in-proximity";
static public readonly StringHash32 Signal_PlayerExitedProximity = "penguin-player-exited-proximity";

public TriggerListener Proximity;
public CollisionListener Collisions;
public Grabbable PlayerGrip;

[NonSerialized] public PenguinBrain CachedBrain;
[NonSerialized] public BitSet32 GrippedShoulders;
[NonSerialized] public SensedObject<PlayerRig> PlayerProximity;

private void Awake() {
this.CacheComponent(ref CachedBrain);

PlayerGrip.OnGrabbed.Register(OnGrabbed);
PlayerGrip.OnReleased.Register(OnReleased);

Proximity.onTriggerEnter.Register(OnProximityEnter);
Proximity.onTriggerExit.Register(OnProximityExit);
}

private void OnDestroy() {
PlayerGrip.OnGrabbed.Deregister(OnGrabbed);
PlayerGrip.OnReleased.Deregister(OnReleased);

Proximity.onTriggerEnter.Deregister(OnProximityEnter);
Proximity.onTriggerExit.Deregister(OnProximityExit);
}

private void OnGrabbed(Grabber hand, int snapIndex) {
Expand All @@ -40,6 +50,7 @@ private void OnGrabbed(Grabber hand, int snapIndex) {

if (GrippedShoulders.Count == 1) {
CachedBrain.Signal(Signal_PlayerGripped);
CachedBrain.Animator.Animator.SetBool("PlayerGripping", true);
}
}

Expand All @@ -51,9 +62,65 @@ private void OnReleased(Grabber hand, int snapIndex) {
GrippedShoulders.Unset(1);
}

if (GrippedShoulders.Count == 1) {
if (GrippedShoulders.Count == 0) {
CachedBrain.Signal(Signal_PlayerReleased);
CachedBrain.Animator.Animator.SetBool("PlayerGripping", false);
}
}

private void OnProximityEnter(Collider collider) {
if (collider.gameObject.layer == LayerMasks.PenguinBody_Index) {
// was penguin
} else {
PlayerRig player = collider.ResolveComponent<PlayerRig>();
if (player != null) {
if (SenseUtility.Increment(ref PlayerProximity, player)) {
CachedBrain.Signal(Signal_PlayerEnteredProximity);
}
}
}
}

private void OnProximityExit(Collider collider) {
if (collider.gameObject.layer == LayerMasks.PenguinBody_Index) {
// was penguin
} else {
PlayerRig player = collider.ResolveComponent<PlayerRig>();
if (player != null) {
if (SenseUtility.Decrement(ref PlayerProximity, player)) {
CachedBrain.Signal(Signal_PlayerEnteredProximity);
}
}
}
}
}

static public class SenseUtility {
static public bool Increment<T>(ref SensedObject<T> sense, T obj) where T : class {
if (sense.Object != obj) {
sense.Object = obj;
sense.Colliders = 0;
}
return (sense.Colliders++ == 0);
}

static public bool Decrement<T>(ref SensedObject<T> sense, T obj) where T : class {
if (sense.Object == obj) {
if (sense.Colliders-- == 1) {
sense.Object = null;
return true;
}
}
return false;
}
}

public struct SensedObject<T> where T : class {
public T Object;
public int Colliders;

static public implicit operator T(SensedObject<T> sensed) {
return sensed.Object;
}
}
}
2 changes: 2 additions & 0 deletions Assets/Code/Penguin/PenguinRelationshipTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace Pennycook {
public sealed class PenguinRelationshipTracker : ScriptActorComponent {
// family
public PenguinBrain Mate;
public PenguinBrain Child;

[NonSerialized] public RingBuffer<PenguinBrain> Children = new RingBuffer<PenguinBrain>(4, RingBufferMode.Expand);
[NonSerialized] public AnalogSignal FamilyAnxiety;

Expand Down
1 change: 1 addition & 0 deletions Assets/Code/Tablet/Highlight/TabletHighlightState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ static public partial class TabletUtility {

// uncomment the default and solid masks to allow for other objects to occlude the target type
public const int DefaultSearchMask = /* LayerMasks.Default_Mask | LayerMasks.Solid_Mask | */ LayerMasks.Grabbable_Mask | LayerMasks.Highlightable_Mask;
public const int CaptureSearchMask = LayerMasks.Solid_Mask | LayerMasks.Grabbable_Mask | LayerMasks.Highlightable_Mask | LayerMasks.CaptureOnly_Mask;
public const int TravelSearchMask = /* LayerMasks.Default_Mask | LayerMasks.Solid_Mask | */ LayerMasks.Warpable_Mask;

public const int CountSearchMask = /* LayerMasks.Default_Mask | LayerMasks.Solid_Mask | */ LayerMasks.Highlightable_Mask | LayerMasks.Countable_Mask;
Expand Down
2 changes: 1 addition & 1 deletion Assets/Code/Tablet/Highlight/TabletHighlightSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public override void ProcessWork(float deltaTime) {
if (Frame.Interval(3) && isGripping && m_StateB.CurrentTool != TabletTool.None && !m_StateA.RaycastJob.IsValid()) {
if (searchMask != 0) {
TabletZoomState zoomState = Find.State<TabletZoomState>();
float coneDistance = 25 * zoomState.ZoomMultiplier;
float coneDistance = m_StateB.CurrentToolDef.RaycastBaseDistance * zoomState.ZoomMultiplier;
float coneRadius = coneDistance * m_StateB.CurrentToolDef.RaycastUnitConeRadius * CameraHelper.UnitHeightForFOV(m_StateA.LookCamera.fieldOfView) / 2;

Log.Trace("cone radius = {0}", coneRadius);
Expand Down
22 changes: 22 additions & 0 deletions Assets/Code/Tablet/Photo/TabletCapturable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,27 @@ namespace Pennycook.Tablet {
public sealed class TabletCapturable : BatchedComponent {
public bool CanCapture = true;
public SerializedHash32 CaptureId;

[Range(0, 1)] public float ViewAlignmentThreshold = 0.5f;
}

public readonly struct CaptureRecord : IEquatable<CaptureRecord> {
public readonly TabletCapturable Source;
public readonly StringHash32 Captured;

public CaptureRecord(TabletCapturable capturable, StringHash32 captureId) {
Source = capturable;
Captured = captureId;
}

public bool Equals(CaptureRecord other) {
return object.ReferenceEquals(Source, other.Source)
&& Captured == other.Captured;
}

public override int GetHashCode() {
return CompareUtils.GetHashCode(Source) << 5
^ CompareUtils.GetHashCode(Captured);
}
}
}
10 changes: 7 additions & 3 deletions Assets/Code/Tablet/Photo/TabletPhotoAnimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ private IEnumerator AnimationSequence(TabletPhotoResult result) {
yield return null;
yield return Offset.Offset0To(default, 0.3f).Ease(Curve.BackOut);

SuccessParticles.Play();

yield return 0.8f;
if (result == TabletPhotoResult.NewBehavior) {
SuccessParticles.Play();
yield return 0.8f;
} else if (result == TabletPhotoResult.BadPhoto) {
yield return Offset.Offset1To(new Vector2(8, 0), 0.3f).Wave(Wave.Function.Cos, 4);
yield return 0.1f;
}

yield return Offset.Offset0To(new Vector2(0, -400), 0.3f).Ease(Curve.QuadIn);

Expand Down
87 changes: 87 additions & 0 deletions Assets/Code/Tablet/Photo/TabletPhotoState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using FieldDay;
using FieldDay.Scripting;
using FieldDay.SharedState;
using Leaf.Runtime;
using UnityEngine;
using UnityEngine.Rendering;

Expand Down Expand Up @@ -83,6 +84,7 @@ static public void TakePhoto(TabletHighlightable highlighted, double ts) {
photoState.NextAllowedPhotoTS = ts + 2;
photoState.QueuedPhoto = photoState.PhotoPool.Alloc();
photoState.QueuedPhoto.Tag = GetPhotoTag(highlighted);
HandleBehaviorCapture(highlighted);
TabletUtility.PlaySfx("Tablet.Photo.Snap");
}

Expand Down Expand Up @@ -132,5 +134,90 @@ static public string GetPhotoTag(TabletHighlightable highlightable) {

return highlightable.gameObject.name;
}

static private Rect CalculatePhotoSubjectRect(TabletHighlightState highlightState, TabletHighlightable highlightable) {
return TabletUtility.CalculateViewportAlignedBoundingBox(highlightable.HighlightCollider.bounds, highlightState.LookCamera, Vector2.one);
}

static private bool DetermineGoodFraming(Rect rect, TabletHighlightState highlightState, TabletCapturable capturable) {
if (rect.width < 0.2f || rect.height < 0.2f) {
Log.Msg("Subject width/height too small");
return false;
}

if ((rect.width * rect.height) < (0.25f * 0.25f)) {
Log.Msg("Subject area too small");
return false;
}

if (capturable && capturable.CanCapture) {
float threshold = capturable.ViewAlignmentThreshold * 2 - 1;
float dot = Vector3.Dot(highlightState.CachedLookCameraTransform.forward, capturable.transform.forward);
if (dot < threshold) {
Log.Msg("Subject not facing camera");
return false;
}
}

Log.Msg("Subject is framed well");
return true;
}

static private TabletPhotoResult HandleBehaviorCapture(TabletHighlightable highlightable) {
if (!highlightable) {
return TabletPhotoResult.Nothing;
}

TabletCapturable cap = highlightable.CachedCapture;
TabletHighlightState highlightState = Find.State<TabletHighlightState>();
Rect framing = CalculatePhotoSubjectRect(highlightState, highlightable);

bool isBadFraming = !DetermineGoodFraming(framing, highlightState, cap);

bool newGlobalBehavior, newUniqueBehavior, wasPerformingBehavior;

if (!isBadFraming && cap && cap.CanCapture && !cap.CaptureId.IsEmpty) {
TabletInventory inv = Find.State<TabletInventory>();
CaptureRecord rec = new CaptureRecord(cap, cap.CaptureId);
newGlobalBehavior = inv.GlobalCapturedBehaviors.Add(cap.CaptureId);
newUniqueBehavior = inv.CaptureRecords.Add(rec);
wasPerformingBehavior = true;
} else {
newGlobalBehavior = newUniqueBehavior = wasPerformingBehavior = false;
}

LeafThreadHandle thread = default;

using (var t = TempVarTable.Alloc()) {
t.ActorInfo(ScriptUtility.Actor(highlightable));
t.Set("behaviorId", cap ? cap.CaptureId : StringHash32.Null);
t.Set("isBadFraming", isBadFraming);
t.Set("wasPerformingBehavior", wasPerformingBehavior);

if (newGlobalBehavior) {
thread = ScriptUtility.Trigger(GameTriggers.TabletNewBehaviorCaptured, t);
}

if (newUniqueBehavior && !thread.IsRunning()) {
thread = ScriptUtility.Trigger(GameTriggers.TabletNewBehaviorInstanceCaptured, t);
} else {
ScriptUtility.Invoke(GameTriggers.TabletNewBehaviorInstanceCaptured, t);
}

if (!thread.IsRunning()) {
thread = ScriptUtility.Trigger(GameTriggers.TabletPhotoTaken, t);
} else {
ScriptUtility.Invoke(GameTriggers.TabletPhotoTaken, t);
}
}

if (newGlobalBehavior) {
return TabletPhotoResult.NewBehavior;
} else if (isBadFraming && wasPerformingBehavior) {
return TabletPhotoResult.BadPhoto;
} else {
return TabletPhotoResult.GoodPhoto;
}
}
}
}
3 changes: 2 additions & 1 deletion Assets/Code/Tablet/TabletInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace Pennycook.Tablet {
public class TabletInventory : SharedStateComponent {
public HashSet<StringHash32> ObservedBehaviors = SetUtils.Create<StringHash32>(32);
public HashSet<StringHash32> GlobalCapturedBehaviors = SetUtils.Create<StringHash32>(32);
public HashSet<CaptureRecord> CaptureRecords = SetUtils.Create<CaptureRecord>(32);
}
}
3 changes: 2 additions & 1 deletion Assets/Code/Tablet/TabletToolData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace Pennycook.Tablet {
public class TabletToolDefinition {
public LayerMask RaycastMask;
public float RaycastBaseDistance = 25;
public float RaycastUnitConeRadius;
public bool ShowReticle = true;

Expand Down Expand Up @@ -115,7 +116,7 @@ static public class TabletToolDefinitions {
};

static public readonly TabletToolDefinition Capture = new TabletToolDefinition() {
RaycastMask = TabletUtility.DefaultSearchMask,
RaycastMask = TabletUtility.CaptureSearchMask,
RaycastUnitConeRadius = 0.4f,

Flags = TabletToolFlags.DoNotUseHighlightBox | TabletToolFlags.InteractionDoesNotRequireHighlight
Expand Down
4 changes: 2 additions & 2 deletions Assets/FieldDay/Audio/AudioMgr.Cmd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ private void FlushCommandPipe() {
}

case AudioCommandType.PlayClipFromName: {
Cmd_PlayFromName(cmd.Play);
Cmd_PlayFromName(m_PlayCommandPipe.Read());
break;
}

case AudioCommandType.PlayClipFromAssetRef: {
Cmd_PlayFromAsset(cmd.Play);
Cmd_PlayFromAsset(m_PlayCommandPipe.Read());
break;
}

Expand Down
10 changes: 7 additions & 3 deletions Assets/FieldDay/Audio/AudioMgr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public struct Config {
private float m_PreloadWorkerTimeSlice;

private Pipe<AudioCommand> m_CommandPipe = new Pipe<AudioCommand>(128, true);
private Pipe<PlayCommandData> m_PlayCommandPipe = new Pipe<PlayCommandData>(64, true);
private UniqueIdAllocator16 m_VoiceIdAllocator = new UniqueIdAllocator16(MaxVoices + MaxBuses);

private Unsafe.ArenaHandle m_Arena;
Expand Down Expand Up @@ -253,10 +254,13 @@ internal void QueueAudioCommand(in AudioCommand cmd) {
m_CommandPipe.Write(cmd);
}

internal AudioHandle QueuePlayAudioCommand(AudioCommand cmd) {
internal AudioHandle QueuePlayAudioCommand(AudioCommandType cmdType, PlayCommandData cmdData) {
UniqueId16 id = m_VoiceIdAllocator.Alloc();
cmd.Play.Handle = id;
m_CommandPipe.Write(cmd);
cmdData.Handle = id;
m_CommandPipe.Write(new AudioCommand() {
Type = cmdType
});
m_PlayCommandPipe.Write(cmdData);
return new AudioHandle(id);
}

Expand Down
Loading

0 comments on commit 073fed1

Please sign in to comment.