raylib/box2d

Licence This example has its own licence, rather than Odin's: raylib/box2d/LICENSE
MIT License Copyright (c) 2023 Erin Catto
The rest of the licence
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Code

main.odin ¶
134 linesSource

1package box2d
2
3import b2 "vendor:box2d"
4import rl "vendor:raylib"
5
6// Original: https://github.com/erincatto/box2d-raylib
7
8Entity :: struct {
9	body_id: b2.BodyId,
10	extent:  b2.Vec2,
11	texture: rl.Texture,
12}
13
14draw_entity :: proc(entity: Entity) {
15	// The boxes were created centered on the bodies, but raylib draws textures starting at the top left corner.
16	// Body_GetWorldPoint gets the top left corner of the box accounting for rotation.
17	p        := b2.Body_GetWorldPoint(entity.body_id, -entity.extent)
18	rotation := b2.Body_GetRotation(entity.body_id)
19	radians  := b2.Rot_GetAngle(rotation)
20
21	rl.DrawTextureEx(entity.texture, p, rl.RAD2DEG * radians, 1., rl.WHITE)
22
23	// I used these circles to ensure the coordinates are correct
24	// rl.DrawCircleV(p, 5., rl.BLACK)
25	// p = b2.Body_GetWorldPoint(entity.body_id, 0)
26	// rl.DrawCircleV(p, 5., rl.BLUE)
27	// p = b2.Body_GetWorldPoint(entity.body_id, entity.extent)
28	// rl.DrawCircleV(p, 5., rl.RED)
29}
30
31GROUND_COUNT :: 14
32BOX_COUNT    :: 10
33
34main :: proc() {
35	width  := i32(1920)
36	height := i32(1080)
37	rl.InitWindow(width, height, "box2d-raylib")
38	defer rl.CloseWindow()
39
40	rl.SetTargetFPS(60)
41
42	// 128 pixels per meter is appropriate for this scene. The boxes are 128 pixels wide.
43	length_units_per_meter := f32(128.)
44	b2.SetLengthUnitsPerMeter(length_units_per_meter)
45
46	world_def := b2.DefaultWorldDef()
47
48	// Realistic gravity is achieved by multiplying gravity by the length unit.
49	world_def.gravity.y = 9.8 * length_units_per_meter
50	world_id := b2.CreateWorld(world_def)
51
52	ground_texture := rl.LoadTexture("ground.png")
53	defer rl.UnloadTexture(ground_texture)
54
55	box_texture := rl.LoadTexture("box.png")
56	defer rl.UnloadTexture(box_texture)
57
58	ground_extent := b2.Vec2{.5 * f32(ground_texture.width), .5 * f32(ground_texture.height)}
59	box_extent    := b2.Vec2{.5 * f32(box_texture.width),    .5 * f32(box_texture.height)}
60
61	// These polygons are centered on the origin and when they are added to a body they
62	// will be centered on the body position.
63	ground_polygon := b2.MakeBox(ground_extent.x, ground_extent.y)
64	box_polygon    := b2.MakeBox(box_extent.x,    box_extent.y)
65
66	ground_entities: [GROUND_COUNT]Entity
67	for &entity, i in ground_entities {
68		body_def := b2.DefaultBodyDef()
69		body_def.position = {(2. * f32(i) + 2.) * ground_extent.x, f32(height) - ground_extent.y - 100.}
70
71		// I used this rotation to test the world to screen transformation
72		// body_def.rotation = b2.MakeRot(.25 * b2.PI * f32(i))
73
74		entity.body_id = b2.CreateBody(world_id, body_def)
75		entity.extent  = ground_extent
76		entity.texture = ground_texture
77		shape_def := b2.DefaultShapeDef()
78		_ = b2.CreatePolygonShape(entity.body_id, shape_def, &ground_polygon)
79	}
80
81	box_entities: [BOX_COUNT]Entity
82	box_index: int
83	for i in 1..=4 {
84		y := f32(height) - box_extent.y - 100. - (2.5 * f32(i) + 2.) * box_extent.y - 20.
85
86		for j in i..=4 {
87			x := .5 * f32(width) + (3. * f32(j) - f32(i) - 3.) * box_extent.x
88
89			body_def := b2.DefaultBodyDef()
90			body_def.type     = .dynamicBody
91			body_def.position = {x, y}
92
93			entity := &box_entities[box_index]
94			entity.body_id = b2.CreateBody(world_id, body_def)
95			entity.texture = box_texture
96			entity.extent  = box_extent
97			shape_def := b2.DefaultShapeDef()
98			_ = b2.CreatePolygonShape(entity.body_id, shape_def, &box_polygon)
99
100			box_index += 1
101		}
102	}
103	assert(box_index == BOX_COUNT)
104
105	pause: bool
106	for !rl.WindowShouldClose() {
107		if rl.IsKeyPressed(.P) {
108			pause = !pause
109		}
110
111		if !pause {
112			delta_time := rl.GetFrameTime()
113			b2.World_Step(world_id, delta_time, 4)
114		}
115
116		rl.BeginDrawing()
117		defer rl.EndDrawing()
118
119		rl.ClearBackground(rl.DARKGRAY)
120
121		message :: "Hello Box2D!"
122		font_size := i32(36)
123		text_width := rl.MeasureText(message, font_size)
124		rl.DrawText(message, (width - text_width) / 2, 50, font_size, rl.LIGHTGRAY)
125
126		for entity in ground_entities {
127			draw_entity(entity)
128		}
129
130		for entity in box_entities {
131			draw_entity(entity)
132		}
133	}
134}

Assets 2

Declarations Used 33