From 1f6e586b2a3fe7f8ca32ce95659032bafef0ad24 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 11 Mar 2025 17:49:04 -0400 Subject: Support external structs with namespaced methods (also C-strings are now `const char*` instead of `char*`) --- examples/game/box.tm | 8 +++---- examples/game/color.tm | 23 ------------------ examples/game/game.tm | 31 ++++++++----------------- examples/game/player.tm | 29 +++++++++++------------ examples/game/raylib.tm | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ examples/game/world.tm | 47 +++++++++++++++++-------------------- 6 files changed, 109 insertions(+), 91 deletions(-) delete mode 100644 examples/game/color.tm create mode 100644 examples/game/raylib.tm (limited to 'examples') diff --git a/examples/game/box.tm b/examples/game/box.tm index acff269e..03294a0a 100644 --- a/examples/game/box.tm +++ b/examples/game/box.tm @@ -1,9 +1,7 @@ # Defines a struct representing boxes on the terrain -use vectors - use ./world.tm -use ./color.tm +use ./raylib.tm -struct Box(pos:Vec2, size=Vec2(50, 50), color=Color.GRAY, blocking=yes): +struct Box(pos:Vector2, size=Vector2(50, 50), color=Color(0x80,0x80,0x80), blocking=yes): func draw(b:Box): - b.color:draw_rectangle(b.pos, b.size) + DrawRectangleRec(Rectangle(b.pos.x, b.pos.y, b.size.x, b.size.y), b.color) diff --git a/examples/game/color.tm b/examples/game/color.tm deleted file mode 100644 index fef12e60..00000000 --- a/examples/game/color.tm +++ /dev/null @@ -1,23 +0,0 @@ -# Defines a struct used to represent colors using 64-bit floats (0.0 - 1.0), -# which can be used to draw colored rectangles in raylib -use -use vectors - -struct Color(r,g,b:Num,a=1.0): - PLAYER := Color(.1,.1,.6,1.) - GRAY := Color(.4,.4,.4) - LIGHT_GRAY := Color(.7,.7,.7) - GOAL := Color(.1,.5,.0) - - func draw_rectangle(c:Color, pos:Vec2, size:Vec2): - inline C { - DrawRectangle( - (int)(_$pos.x), (int)(_$pos.y), (int)(_$size.x), (int)(_$size.y), - ((Color){ - (int8_t)(uint8_t)(255.*_$c.r), - (int8_t)(uint8_t)(255.*_$c.g), - (int8_t)(uint8_t)(255.*_$c.b), - (int8_t)(uint8_t)(255.*_$c.a), - }) - ); - } diff --git a/examples/game/game.tm b/examples/game/game.tm index 86e4e01c..e0bf7b32 100644 --- a/examples/game/game.tm +++ b/examples/game/game.tm @@ -1,40 +1,29 @@ # This game demo uses Raylib to present a simple -use libraylib.so -use -use - +use ./raylib.tm use ./world.tm func main(map=(./map.txt)): - extern InitWindow:func(w:Int32, h:Int32, title:CString) InitWindow(1600, 900, CString("raylib [core] example - 2d camera")) map_contents := map:read() or exit("Could not find the game map: $map") - World.CURRENT:load_map(map_contents) + world := @World( + player=@Player(Vector2(0,0), Vector2(0,0)), + goal=@Box(Vector2(0,0), Vector2(50,50), color=Color(0x10,0xa0,0x10)), + boxes=@[:@Box], + ) + world:load_map(map_contents) - extern SetTargetFPS:func(fps:Int32) SetTargetFPS(60) - extern WindowShouldClose:func(->Bool) - while not WindowShouldClose(): - extern GetFrameTime:func(->Num32) dt := GetFrameTime() - World.CURRENT:update(Num(dt)) + world:update(dt) - extern BeginDrawing:func() BeginDrawing() - - inline C { - ClearBackground((Color){0xCC, 0xCC, 0xCC, 0xFF}); - } - - World.CURRENT:draw() - - extern EndDrawing:func() + ClearBackground(Color(0xCC, 0xCC, 0xCC, 0xFF)) + world:draw() EndDrawing() - extern CloseWindow:func() CloseWindow() diff --git a/examples/game/player.tm b/examples/game/player.tm index a5c39a8d..dac2508a 100644 --- a/examples/game/player.tm +++ b/examples/game/player.tm @@ -1,25 +1,22 @@ # Defines a struct representing the player, which is controlled by WASD keys -use libraylib.so -use -use - -use vectors use ./world.tm +use ./raylib.tm -struct Player(pos,prev_pos:Vec2): - WALK_SPEED := 500. - ACCEL := 0.3 - FRICTION := 0.99 - SIZE := Vec2(30, 30) +struct Player(pos,prev_pos:Vector2): + WALK_SPEED := Num32(500.) + ACCEL := Num32(0.3) + FRICTION := Num32(0.99) + SIZE := Vector2(30, 30) + COLOR := Color(0x60, 0x60, 0xbF) func update(p:@Player): - target_x := inline C:Num { - (Num_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) + target_x := inline C:Num32 { + (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) } - target_y := inline C:Num { - (Num_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) + target_y := inline C:Num32 { + (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0)) } - target_vel := Vec2(target_x, target_y):norm() * Player.WALK_SPEED + target_vel := Vector2(target_x, target_y):norm() * Player.WALK_SPEED vel := (p.pos - p.prev_pos)/World.DT vel *= Player.FRICTION @@ -28,4 +25,4 @@ struct Player(pos,prev_pos:Vec2): p.prev_pos, p.pos = p.pos, p.pos + World.DT*vel func draw(p:Player): - Color.PLAYER:draw_rectangle(p.pos, Player.SIZE) + DrawRectangleRec(Rectangle(p.pos.x, p.pos.y, Player.SIZE.x, Player.SIZE.y), Player.COLOR) diff --git a/examples/game/raylib.tm b/examples/game/raylib.tm new file mode 100644 index 00000000..2721d0e1 --- /dev/null +++ b/examples/game/raylib.tm @@ -0,0 +1,62 @@ +# Raylib wrapper for some functions and structs +use libraylib.so +use +use + +struct Color(r,g,b:Byte,a=Byte(255); extern) +struct Rectangle(x,y,width,height:Num32; extern): + func draw(r:Rectangle, color:Color): + DrawRectangleRec(r, color) + +struct Vector2(x,y:Num32; extern): + ZERO := Vector2(0, 0) + func plus(a,b:Vector2->Vector2; inline): + return Vector2(a.x+b.x, a.y+b.y) + func minus(a,b:Vector2->Vector2; inline): + return Vector2(a.x-b.x, a.y-b.y) + func times(a,b:Vector2->Vector2; inline): + return Vector2(a.x*b.x, a.y*b.y) + func negative(v:Vector2->Vector2; inline): + return Vector2(-v.x, -v.y) + func dot(a,b:Vector2->Num32; inline): + return ((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y))! + func cross(a,b:Vector2->Num32; inline): + return a.x*b.y - a.y*b.x + func scaled_by(v:Vector2, k:Num32->Vector2; inline): + return Vector2(v.x*k, v.y*k) + func divided_by(v:Vector2, divisor:Num32->Vector2; inline): + return Vector2(v.x/divisor, v.y/divisor) + func length(v:Vector2->Num32; inline): + return (v.x*v.x + v.y*v.y)!:sqrt() + func dist(a,b:Vector2->Num32; inline): + return a:minus(b):length() + func angle(v:Vector2->Num32; inline): + return Num32.atan2(v.y, v.x) + func norm(v:Vector2->Vector2; inline): + if v.x == 0 and v.y == 0: + return v + len := v:length() + return Vector2(v.x/len, v.y/len) + func rotated(v:Vector2, radians:Num32 -> Vector2): + cos := radians:cos() or return v + sin := radians:sin() or return v + return Vector2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) + func mix(a,b:Vector2, amount:Num32 -> Vector2): + return Vector2( + amount:mix(a.x, b.x), + amount:mix(a.y, b.y), + ) + +extern InitWindow:func(width:Int32, height:Int32, title:CString) +extern SetTargetFPS:func(fps:Int32) +extern WindowShouldClose:func(->Bool) +extern GetFrameTime:func(->Num32) +extern BeginDrawing:func() +extern EndDrawing:func() +extern CloseWindow:func() +extern ClearBackground:func(color:Color) +extern DrawRectangle:func(x,y,width,height:Int32, color:Color) +extern DrawRectangleRec:func(rec:Rectangle, color:Color) +extern DrawText:func(text:CString, x,y:Int32, text_height:Int32, color:Color) +extern GetScreenWidth:func(->Int32) +extern GetScreenHeight:func(->Int32) diff --git a/examples/game/world.tm b/examples/game/world.tm index f1b65a87..864bd8d2 100644 --- a/examples/game/world.tm +++ b/examples/game/world.tm @@ -1,12 +1,10 @@ -use vectors - use ./player.tm -use ./color.tm +use ./raylib.tm use ./box.tm # Return a displacement relative to `a` that will push it out of `b` -func solve_overlap(a_pos:Vec2, a_size:Vec2, b_pos:Vec2, b_size:Vec2 -> Vec2): +func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Vector2): a_left := a_pos.x a_right := a_pos.x + a_size.x a_top := a_pos.y @@ -23,27 +21,26 @@ func solve_overlap(a_pos:Vec2, a_size:Vec2, b_pos:Vec2, b_size:Vec2 -> Vec2): # If either axis is not overlapping, then there is no collision: if overlap_x <= 0 or overlap_y <= 0: - return Vec2(0, 0) + return Vector2(0, 0) if overlap_x < overlap_y: if a_right > b_left and a_right < b_right: - return Vec2(-(overlap_x), 0) + return Vector2(-(overlap_x), 0) else if a_left < b_right and a_left > b_left: - return Vec2(overlap_x, 0) + return Vector2(overlap_x, 0) else: if a_top < b_bottom and a_top > b_top: - return Vec2(0, overlap_y) + return Vector2(0, overlap_y) else if a_bottom > b_top and a_bottom < b_bottom: - return Vec2(0, -overlap_y) + return Vector2(0, -overlap_y) - return Vec2(0, 0) + return Vector2(0, 0) -struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): - DT := 1./60. - CURRENT := @World(@Player(Vec2(0,0), Vec2(0,0)), @Box(Vec2(0,0), Vec2(0,0), Color.GOAL), @[:@Box]) - STIFFNESS := 0.3 +struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won=no): + DT := (Num32(1.)/Num32(60.))! + STIFFNESS := Num32(0.3) - func update(w:@World, dt:Num): + func update(w:@World, dt:Num32): w.dt_accum += dt while w.dt_accum > 0: w:update_once() @@ -52,7 +49,7 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): func update_once(w:@World): w.player:update() - if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vec2(0,0): + if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vector2(0,0): w.won = yes # Resolve player overlapping with any boxes: @@ -67,26 +64,24 @@ struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=0.0, won=no): w.player:draw() if w.won: - inline C { - DrawText("WINNER", GetScreenWidth()/2-48*3, GetScreenHeight()/2-24, 48, (Color){0,0,0,0xFF}); - } + DrawText(CString("WINNER"), GetScreenWidth()/Int32(2)-Int32(48*3), GetScreenHeight()/Int32(2)-Int32(24), 48, Color(0,0,0)) func load_map(w:@World, map:Text): if map:has($/[]/): map = map:replace_all({$/[]/="#", $/@{1..}/="@", $/ /=" "}) w.boxes = @[:@Box] - box_size := Vec2(50., 50.) + box_size := Vector2(50., 50.) for y,line in map:lines(): for x,cell in line:split(): if cell == "#": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - box := @Box(pos, size=box_size, color=Color.GRAY) + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + box := @Box(pos, size=box_size, color=Color(0x80,0x80,0x80)) w.boxes:insert(box) else if cell == "@": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - pos += box_size/2. - Player.SIZE/2. + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + pos += box_size/Num32(2) - Player.SIZE/Num32(2) w.player = @Player(pos,pos) else if cell == "?": - pos := Vec2((Num(x)-1) * box_size.x, (Num(y)-1) * box_size.y) - w.goal = @Box(pos, size=box_size, color=Color.GOAL) + pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) + w.goal.pos = pos -- cgit v1.2.3