1
+ // SPDX-FileCopyrightText: 2025 Erin Catto
2
+ // SPDX-FileCopyrightText: 2025 Ikpil Choi([email protected] )
3
+ // SPDX-License-Identifier: MIT
4
+
5
+ using Silk . NET . GLFW ;
6
+ using static Box2D . NET . B2Ids ;
7
+ using static Box2D . NET . B2Geometries ;
8
+ using static Box2D . NET . B2Types ;
9
+ using static Box2D . NET . B2MathFunction ;
10
+ using static Box2D . NET . B2Bodies ;
11
+ using static Box2D . NET . B2Shapes ;
12
+ using static Box2D . NET . B2Worlds ;
13
+ using static Box2D . NET . B2Contacts ;
14
+ using static Box2D . NET . B2Joints ;
15
+
16
+ namespace Box2D . NET . Samples . Samples . Events ;
17
+
18
+ // This shows how to create a projectile that explodes on impact
19
+ class ProjectileEvent : Sample
20
+ {
21
+ private static readonly int sampleProjectileEvent = SampleFactory . Shared . RegisterSample ( "Events" , "Projectile Event" , Create ) ;
22
+
23
+ private B2BodyId m_projectileId ;
24
+ private B2ShapeId m_projectileShapeId ;
25
+ private B2Vec2 m_point1 ;
26
+ private B2Vec2 m_point2 ;
27
+ private bool m_dragging ;
28
+
29
+ private static Sample Create ( SampleContext context )
30
+ {
31
+ return new ProjectileEvent ( context ) ;
32
+ }
33
+
34
+ public ProjectileEvent ( SampleContext context ) : base ( context )
35
+ {
36
+ if ( m_context . settings . restart == false )
37
+ {
38
+ m_context . camera . m_center = new B2Vec2 ( - 7.0f , 9.0f ) ;
39
+ m_context . camera . m_zoom = 14.0f ;
40
+ }
41
+
42
+ {
43
+ B2BodyDef bodyDef = b2DefaultBodyDef ( ) ;
44
+ bodyDef . position = new B2Vec2 ( 0.0f , 0.0f ) ;
45
+ B2BodyId groundId = b2CreateBody ( m_worldId , ref bodyDef ) ;
46
+
47
+ B2ShapeDef shapeDef = b2DefaultShapeDef ( ) ;
48
+ shapeDef . enableSensorEvents = true ;
49
+
50
+ B2Segment segment = new B2Segment ( new B2Vec2 ( 10.0f , 0.0f ) , new B2Vec2 ( 10.0f , 20.0f ) ) ;
51
+ b2CreateSegmentShape ( groundId , ref shapeDef , ref segment ) ;
52
+
53
+ segment = new B2Segment ( new B2Vec2 ( - 30.0f , 0.0f ) , new B2Vec2 ( 30.0f , 0.0f ) ) ;
54
+ b2CreateSegmentShape ( groundId , ref shapeDef , ref segment ) ;
55
+ }
56
+
57
+ m_projectileId = new B2BodyId ( ) ;
58
+ m_projectileShapeId = new B2ShapeId ( ) ;
59
+ m_dragging = false ;
60
+ m_point1 = b2Vec2_zero ;
61
+ m_point2 = b2Vec2_zero ;
62
+
63
+ {
64
+ B2Polygon box = b2MakeRoundedBox ( 0.45f , 0.45f , 0.05f ) ;
65
+
66
+ B2ShapeDef shapeDef = b2DefaultShapeDef ( ) ;
67
+ shapeDef . enableSensorEvents = true ;
68
+
69
+ float offset = 0.01f ;
70
+
71
+ float x = 8.0f ;
72
+ B2BodyDef bodyDef = b2DefaultBodyDef ( ) ;
73
+ bodyDef . type = B2BodyType . b2_dynamicBody ;
74
+
75
+ for ( int i = 0 ; i < 8 ; ++ i )
76
+ {
77
+ float shift = ( i % 2 == 0 ? - offset : offset ) ;
78
+ bodyDef . position = new B2Vec2 ( x + shift , 0.5f + 1.0f * i ) ;
79
+
80
+ B2BodyId bodyId = b2CreateBody ( m_worldId , ref bodyDef ) ;
81
+
82
+ b2CreatePolygonShape ( bodyId , ref shapeDef , ref box ) ;
83
+ }
84
+ }
85
+ }
86
+
87
+ void FireProjectile ( )
88
+ {
89
+ if ( B2_IS_NON_NULL ( m_projectileId ) )
90
+ {
91
+ b2DestroyBody ( m_projectileId ) ;
92
+ }
93
+
94
+ B2BodyDef bodyDef = b2DefaultBodyDef ( ) ;
95
+ bodyDef . type = B2BodyType . b2_dynamicBody ;
96
+ bodyDef . position = m_point1 ;
97
+ bodyDef . linearVelocity = 4.0f * ( m_point2 - m_point1 ) ;
98
+ bodyDef . isBullet = true ;
99
+
100
+ m_projectileId = b2CreateBody ( m_worldId , ref bodyDef ) ;
101
+
102
+ B2Circle circle = new B2Circle ( new B2Vec2 ( 0.0f , 0.0f ) , 0.25f ) ;
103
+ B2ShapeDef shapeDef = b2DefaultShapeDef ( ) ;
104
+ shapeDef . enableContactEvents = true ;
105
+ m_projectileShapeId = b2CreateCircleShape ( m_projectileId , ref shapeDef , ref circle ) ;
106
+ }
107
+
108
+ public override void MouseDown ( B2Vec2 p , MouseButton button , KeyModifiers mod )
109
+ {
110
+ if ( button == MouseButton . Left )
111
+ {
112
+ if ( mod == KeyModifiers . Control )
113
+ {
114
+ m_dragging = true ;
115
+ m_point1 = p ;
116
+ }
117
+ }
118
+ }
119
+
120
+ public override void MouseUp ( B2Vec2 p , MouseButton button )
121
+ {
122
+ if ( button == MouseButton . Left )
123
+ {
124
+ if ( m_dragging )
125
+ {
126
+ m_dragging = false ;
127
+ FireProjectile ( ) ;
128
+ }
129
+ }
130
+ }
131
+
132
+ public override void MouseMove ( B2Vec2 p )
133
+ {
134
+ if ( m_dragging )
135
+ {
136
+ m_point2 = p ;
137
+ }
138
+ }
139
+
140
+ public override void Step ( )
141
+ {
142
+ base . Step ( ) ;
143
+
144
+
145
+ B2ContactEvents contactEvents = b2World_GetContactEvents ( m_worldId ) ;
146
+ for ( int i = 0 ; i < contactEvents . beginCount ; ++ i )
147
+ {
148
+ ref B2ContactBeginTouchEvent @event = ref contactEvents . beginEvents [ i ] ;
149
+
150
+ if ( B2_ID_EQUALS ( @event . shapeIdA , m_projectileShapeId ) || B2_ID_EQUALS ( @event . shapeIdB , m_projectileShapeId ) )
151
+ {
152
+ if ( b2Contact_IsValid ( @event . contactId ) )
153
+ {
154
+ B2ContactData data = b2Contact_GetData ( @event . contactId ) ;
155
+
156
+ if ( data . manifold . pointCount > 0 )
157
+ {
158
+ B2ExplosionDef explosionDef = b2DefaultExplosionDef ( ) ;
159
+ explosionDef . position = data . manifold . points [ 0 ] . point ;
160
+ explosionDef . radius = 1.0f ;
161
+ explosionDef . impulsePerLength = 20.0f ;
162
+ b2World_Explode ( m_worldId , ref explosionDef ) ;
163
+
164
+ b2DestroyBody ( m_projectileId ) ;
165
+ m_projectileId = b2_nullBodyId ;
166
+ }
167
+ }
168
+
169
+ break ;
170
+ }
171
+ }
172
+ }
173
+
174
+ public override void Draw ( Settings settings )
175
+ {
176
+ base . Draw ( settings ) ;
177
+
178
+ DrawTextLine ( "Use Ctrl + Left Mouse to drag and shoot a projectile" ) ;
179
+
180
+ if ( m_dragging )
181
+ {
182
+ m_draw . DrawLine ( m_point1 , m_point2 , B2HexColor . b2_colorWhite ) ;
183
+ m_draw . DrawPoint ( m_point1 , 5.0f , B2HexColor . b2_colorGreen ) ;
184
+ m_draw . DrawPoint ( m_point2 , 5.0f , B2HexColor . b2_colorRed ) ;
185
+ }
186
+ }
187
+ }
0 commit comments