Skip to content

Commit 2704289

Browse files
committed
Add "Custom Filter" scene.
1 parent f416bc5 commit 2704289

File tree

9 files changed

+471
-0
lines changed

9 files changed

+471
-0
lines changed

LowLevel/Sandbox/Assets/Sandbox.unity

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ MonoBehaviour:
404404
Name: Conveyor Belt
405405
Category: Shapes
406406
Description: Demonstrates the use of the Tangent Speed surface material property.
407+
- ScenePath: Assets/Scenes/Shapes/CustomFilter/CustomFilter.unity
408+
Name: Custom Filter
409+
Category: Shapes
410+
Description: Demonstrates the use of a custom shape filter.
407411
- ScenePath: Assets/Scenes/Shapes/EllipsePolygons/EllipsePolygons.unity
408412
Name: Ellipse Polygons
409413
Category: Shapes

LowLevel/Sandbox/Assets/Scenes/Shapes/CustomFilter.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using Unity.Collections;
2+
using UnityEngine;
3+
using UnityEngine.LowLevelPhysics2D;
4+
using UnityEngine.UIElements;
5+
6+
public class CustomFilter : MonoBehaviour, PhysicsCallbacks.IContactFilterCallback
7+
{
8+
private SandboxManager m_SandboxManager;
9+
private SceneManifest m_SceneManifest;
10+
private UIDocument m_UIDocument;
11+
private CameraManipulator m_CameraManipulator;
12+
13+
private PhysicsWorld m_PhysicsWorld;
14+
private bool m_OldContactFilterCallbacks;
15+
16+
private Button m_SpawnButton;
17+
private PhysicsBody m_ConveyorBeltBody;
18+
private PhysicsShape m_ConveyorBeltShape;
19+
20+
private void OnEnable()
21+
{
22+
m_SandboxManager = FindFirstObjectByType<SandboxManager>();
23+
m_SceneManifest = FindFirstObjectByType<SceneManifest>();
24+
m_UIDocument = GetComponent<UIDocument>();
25+
26+
m_CameraManipulator = FindFirstObjectByType<CameraManipulator>();
27+
m_CameraManipulator.CameraSize = 12;
28+
m_CameraManipulator.CameraPosition = new Vector2(0f, 7f);
29+
30+
// Set up the scene reset action.
31+
m_SandboxManager.SceneResetAction = SetupScene;
32+
33+
// Set Overrides.
34+
m_SandboxManager.SetOverrideColorShapeState(false);
35+
36+
m_PhysicsWorld = PhysicsWorld.defaultWorld;
37+
38+
// Ensure the contact filter callbacks are on.
39+
m_OldContactFilterCallbacks = m_PhysicsWorld.contactFilterCallbacks;
40+
m_PhysicsWorld.contactFilterCallbacks = true;
41+
42+
SetupOptions();
43+
44+
SetupScene();
45+
}
46+
47+
private void OnDisable()
48+
{
49+
// Restore global default.
50+
m_PhysicsWorld.contactFilterCallbacks = m_OldContactFilterCallbacks;
51+
52+
// Reset overrides.
53+
m_SandboxManager.ResetOverrideColorShapeState();
54+
}
55+
56+
private void SetupOptions()
57+
{
58+
var root = m_UIDocument.rootVisualElement;
59+
60+
{
61+
// Menu Region (for camera manipulator).
62+
var menuRegion = root.Q<VisualElement>("menu-region");
63+
menuRegion.RegisterCallback<PointerEnterEvent>(_ => ++m_CameraManipulator.OverlapUI);
64+
menuRegion.RegisterCallback<PointerLeaveEvent>(_ => --m_CameraManipulator.OverlapUI);
65+
66+
// Reset Scene.
67+
var resetScene = root.Q<Button>("reset-scene");
68+
resetScene.clicked += SetupScene;
69+
70+
// Fetch the scene description.
71+
var sceneDescription = root.Q<Label>("scene-description");
72+
sceneDescription.text = $"\"{m_SceneManifest.LoadedSceneName}\"\n{m_SceneManifest.LoadedSceneDescription}";
73+
}
74+
}
75+
76+
private void SetupScene()
77+
{
78+
// Reset the scene state.
79+
m_SandboxManager.ResetSceneState();
80+
81+
var bodies = m_SandboxManager.Bodies;
82+
83+
// Ground.
84+
{
85+
var body = m_PhysicsWorld.CreateBody();
86+
bodies.Add(body);
87+
using var vertices = new NativeList<Vector2>(Allocator.Temp)
88+
{
89+
new(-20f, 0f),
90+
new(-20f, 10f),
91+
new(20f, 10f),
92+
new(20f, 0f)
93+
};
94+
body.CreateChain(new ChainGeometry(vertices.AsArray()), PhysicsChainDefinition.defaultDefinition);
95+
}
96+
97+
// Create even/odd bodies.
98+
{
99+
var geometry = PolygonGeometry.CreateBox(Vector2.one * 2f, 0.2f);
100+
var shapeDef = new PhysicsShapeDefinition { contactFilterCallbacks = true };
101+
102+
for (var n = 0; n < 15; ++n)
103+
{
104+
var body = m_PhysicsWorld.CreateBody(new PhysicsBodyDefinition { bodyType = RigidbodyType2D.Dynamic, gravityScale = 4f, position = new Vector2(-17.5f + n * 2.5f, 4f) });
105+
106+
var colorGroup = n % 3;
107+
108+
// Set the color appropriately.
109+
shapeDef.surfaceMaterial.customColor = colorGroup switch
110+
{
111+
0 => Color.red,
112+
1 => Color.green,
113+
2 => Color.blue,
114+
_ => default
115+
};
116+
117+
var shape = body.CreateShape(geometry, shapeDef);
118+
119+
// Set the custom data.
120+
shape.userData = new PhysicsUserData { intValue = colorGroup, boolValue = true };
121+
122+
// Set the callback target.
123+
shape.callbackTarget = this;
124+
}
125+
}
126+
}
127+
128+
// Called when any shapes that are asking for a contact filter potentially come into contact.
129+
// This must be thread-safe and should NOT perform any write operations to the physics engine!
130+
public bool OnContactFilter2D(PhysicsEvents.ContactFilterEvent contactFilterEvent)
131+
{
132+
// Fetch the user-data from the shapes.
133+
// NOTE: This is a (thread) safe way to pass custom-data.
134+
var userData1 = contactFilterEvent.shapeA.userData;
135+
var userData2 = contactFilterEvent.shapeB.userData;
136+
137+
// Allow contact if this isn't a filtered shape pair.
138+
if (!userData1.boolValue || !userData2.boolValue)
139+
return true;
140+
141+
// Allow contact if in the same "color group".
142+
return userData1.intValue == userData2.intValue;
143+
}
144+
}

LowLevel/Sandbox/Assets/Scenes/Shapes/CustomFilter/CustomFilter.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)