Compare commits

..

7 commits

Author SHA1 Message Date
883c28363e
get player dir sprite swapping working
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
2026-05-14 12:04:51 +01:00
fe99c7a4a6
enable zoom in the camera
"
"
"
"
"
"
"
"
"
"
2026-05-14 10:51:38 +01:00
00a999caf4
enable reverse driving 2026-05-14 09:44:13 +01:00
9032f44830
vibe code differential movment
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
2026-05-14 09:34:45 +01:00
8e54b39b18
try and get player steering working
"
"
"
"
"
"
"
"
"
"
"
"
2026-05-13 22:34:46 +01:00
26e88c8f03
init defold game 2026-05-13 18:16:55 +01:00
925d375313
render radar boxes 2026-05-10 17:02:37 +02:00
22 changed files with 459 additions and 30 deletions

4
.gitignore vendored
View file

@ -2,3 +2,7 @@
.clj-kondo/
.lsp/
build/
.internal

View file

@ -0,0 +1,86 @@
{
:code {
:breakpoints [
]
}
:workflow {
:open-tabs [
[
[
"/main/main.collection"
:scene
]
[
"/main/player/player.script"
:code
]
]
]
:recent-files [
[
"/main/player.tilesource"
:text
]
[
"/main/player.sprite"
:text
]
[
"/main/main.script"
:code
]
[
"/main/main.collection"
:text
]
[
"/main/player.sprite"
:scene
]
[
"/main/main.atlas"
:scene
]
[
"/main/player.tilesource"
:scene
]
[
"/main/player.script"
:code
]
[
"/main/zoom.render_script"
:code
]
[
"/builtins/render/default.render_script"
:code
]
[
"/builtins/render/default.render"
:cljfx-form-view
]
[
"/input/game.input_binding"
:cljfx-form-view
]
[
"/game.project"
:cljfx-form-view
]
[
"/main/zoom.script"
:code
]
[
"/main/main.collection"
:scene
]
[
"/main/player/player.script"
:code
]
]
}
}

19
defold_game/README.md Normal file
View file

@ -0,0 +1,19 @@
# Welcome to Defold
This project was created from the "desktop" project template. This means that the settings in ["game.project"](defold://open?path=/game.project) have been changed to be suitable for a desktop game:
- The screen size is set to 1280x720
- Projection is set to Fixed Fit
- macOS and Windows icons are set
- Mouse clicks are bound to action "touch"
- A simple script in a game object is set up to receive and react to input
[Build and run](defold://project.build) to see it in action. You can of course alter these settings to fit your needs.
Check out [the documentation pages](https://defold.com/learn) for examples, tutorials, manuals and API docs.
If you run into trouble, help is available in [our forum](https://forum.defold.com).
Happy Defolding!
---

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

35
defold_game/game.project Normal file
View file

@ -0,0 +1,35 @@
[project]
title = Desktop Game
[bootstrap]
main_collection = /main/main.collectionc
[input]
game_binding = /input/game.input_bindingc
use_accelerometer = 0
[display]
width = 1280
height = 720
high_dpi = 1
[script]
shared_state = 1
[windows]
app_icon = /assets/app_icons/appicon.ico
[osx]
app_icon = /assets/app_icons/appicon.icns
[android]
input_method = HiddenInputField
[physics]
gravity_y = -1000.0
scale = 0.01
velocity_threshold = 100.0
[html5]
scale_mode = stretch

View file

@ -0,0 +1,20 @@
key_trigger {
input: KEY_A
action: "a_key"
}
key_trigger {
input: KEY_Q
action: "q_key"
}
key_trigger {
input: KEY_D
action: "d_key"
}
key_trigger {
input: KEY_E
action: "e_key"
}
mouse_trigger {
input: MOUSE_BUTTON_LEFT
action: "touch"
}

View file

@ -0,0 +1,15 @@
images {
image: "/assets/images/background.png"
}
images {
image: "/assets/images/logo.png"
}
images {
image: "/builtins/graphics/particle_blob.png"
}
images {
image: "/assets/images/player.png"
pivot_x: 0.536806
pivot_y: 0.447809
}
extrude_borders: 2

View file

@ -0,0 +1,42 @@
name: "main"
scale_along_z: 0
embedded_instances {
id: "camera"
data: "embedded_components {\n"
" id: \"camera\"\n"
" type: \"camera\"\n"
" data: \"aspect_ratio: 1.0\\n"
"fov: 0.7854\\n"
"near_z: -1.0\\n"
"far_z: 1.0\\n"
"orthographic_projection: 1\\n"
"orthographic_zoom: 2.0\\n"
"\"\n"
"}\n"
""
position {
x: 640.0
y: 360.0
}
}
embedded_instances {
id: "player"
children: "body"
data: "components {\n"
" id: \"player_controller\"\n"
" component: \"/main/player/player.script\"\n"
"}\n"
""
position {
x: 649.0
y: 503.0
}
}
embedded_instances {
id: "body"
data: "components {\n"
" id: \"body\"\n"
" component: \"/main/player/player.sprite\"\n"
"}\n"
""
}

View file

@ -0,0 +1,10 @@
-- function init(self)
-- msg.post(".", "acquire_input_focus")
-- end
--
-- function on_input(self, action_id, action)
-- if action_id == hash("touch") and action.pressed then
-- print("Touch!")
-- end
-- end
--

View file

@ -0,0 +1,86 @@
local ACCELERATION = 100
local DECELERATION = 200
local MAX_SPEED = 400
local WHEEL_BASE = 80
local UP = vmath.vector3(0, 1, 0)
local DIRECTIONS = { "n", "ne", "e", "se", "s", "sw", "w", "nw" }
local function get_direction(rot)
local forward = vmath.rotate(rot, UP)
local angle = math.deg(math.atan2(forward.x, forward.y))
if angle < 0 then angle = angle + 360 end
local index = math.floor((angle + 22.5) / 45) % 8 + 1
return DIRECTIONS[index]
end
function init(self)
msg.post(".", "acquire_input_focus")
self.left_speed = 0
self.right_speed = 0
self.current_anim = nil
self.left_fwd = false
self.left_rev = false
self.right_fwd = false
self.right_rev = false
end
local function update_speed(speed, drive, dt)
if drive ~= 0 then
local target = drive * MAX_SPEED
local step = ACCELERATION * dt
if speed < target then
return math.min(speed + step, target)
else
return math.max(speed - step, target)
end
else
local step = DECELERATION * dt
if speed > 0 then
return math.max(speed - step, 0)
else
return math.min(speed + step, 0)
end
end
end
function update(self, dt)
local left_drive = (self.left_fwd and 1 or 0) - (self.left_rev and 1 or 0)
local right_drive = (self.right_fwd and 1 or 0) - (self.right_rev and 1 or 0)
self.left_speed = update_speed(self.left_speed, left_drive, dt)
self.right_speed = update_speed(self.right_speed, right_drive, dt)
-- differential drive: speed difference creates rotation, average creates forward motion
local linear_speed = (self.left_speed + self.right_speed) / 2
local angular_vel = (self.right_speed - self.left_speed) / WHEEL_BASE
-- pprint({rot_changes = angular_vel * dt})
local rot = go.get_rotation()
rot = rot * vmath.quat_rotation_z(angular_vel * dt)
go.set_rotation(rot)
go.set_rotation(vmath.conj(rot), "body")
local p = go.get_position()
p = p + vmath.rotate(rot, UP * linear_speed * dt)
go.set_position(p)
-- switch animation when facing direction changes
local dir = get_direction(rot)
if dir ~= self.current_anim then
self.current_anim = dir
msg.post("body#body", "play_animation", { id = hash(dir) })
end
end
function on_input(self, action_id, action)
local held = action.pressed or not action.released
if action_id == hash("a_key") then self.left_fwd = held
elseif action_id == hash("q_key") then self.left_rev = held
elseif action_id == hash("d_key") then self.right_fwd = held
elseif action_id == hash("e_key") then self.right_rev = held
end
end

View file

@ -0,0 +1,6 @@
default_animation: "n"
material: "/builtins/materials/sprite.material"
textures {
sampler: "texture_sampler"
texture: "/main/player/player.tilesource"
}

View file

@ -0,0 +1,52 @@
image: "/assets/images/player.png"
tile_width: 25
tile_height: 25
collision_groups: "player"
animations {
id: "e"
start_tile: 4
end_tile: 4
playback: PLAYBACK_NONE
}
animations {
id: "n"
start_tile: 1
end_tile: 1
playback: PLAYBACK_NONE
}
animations {
id: "ne"
start_tile: 3
end_tile: 3
playback: PLAYBACK_NONE
}
animations {
id: "nw"
start_tile: 8
end_tile: 8
playback: PLAYBACK_NONE
}
animations {
id: "s"
start_tile: 2
end_tile: 2
playback: PLAYBACK_NONE
}
animations {
id: "se"
start_tile: 5
end_tile: 5
playback: PLAYBACK_NONE
}
animations {
id: "sw"
start_tile: 6
end_tile: 6
playback: PLAYBACK_NONE
}
animations {
id: "w"
start_tile: 7
end_tile: 7
playback: PLAYBACK_NONE
}

View file

@ -31,7 +31,8 @@
(local height 15)
(local player {
:x 50 :y 50 :speed 80 :battery 100 :rot 0
:vision-pts []
:vision-pts [] ; the points that make a polygon around the player's cone of vision
:vision-hitboxes [] ; the hitboxes that are visible to the player's cone of vision
:hitbox [50 50 width height]
:ever-moved false ; useful for inital update
})
@ -74,6 +75,7 @@
(let [pts (self:cal-vision-points)
(ox oy) (self:origin-pt)
poly-pts [ox oy]
visable-hitboxes []
]
(each [_ pt (ipairs pts)]
(let [
@ -81,12 +83,14 @@
first-item (. items 1)]
(if first-item
(do
(table.insert visable-hitboxes (. first-item :item))
(table.insert poly-pts (. first-item :x1))
(table.insert poly-pts (. first-item :y1)))
(do
(table.insert poly-pts (. pt 1))
(table.insert poly-pts (. pt 2))))))
(set self.vision-pts poly-pts)
(set self.vision-hitboxes visable-hitboxes)
))

View file

@ -61,7 +61,7 @@
camera
walls
fog-of-war
radar
; radar
(hud {: screen})
]
})]

View file

@ -5,7 +5,7 @@
(local camera {:x 0 :y 0})
(fn camera.draw10 [self]
(fn camera.draw25 [self]
(love.graphics.origin)
(love.graphics.translate (* -1 self.x) (* -1 self.y)))

View file

@ -7,15 +7,22 @@
(local fow
{
:player-vision-pts []
:player-pos [0 0]
:player-vision-hitboxes []
:player-pt [0 0]
:player-origin-pt [0 0]
:font nil
:canvas nil })
:canvas nil
:main-canvas nil
})
(lambda fow.load [self]
(set self.canvas (love.graphics.newCanvas 1000 1000))
(beholder.observe "PLAYER.MOVED" (lambda [player]
(set self.player-pos [player.x player.y])
(set self.player-vision-pts player.vision-pts)))
(let [(ox oy) (player:origin-pt)]
(set self.player-origin-pt [ox oy]))
(set self.player-pt [player.x player.y])
(set self.player-vision-hitboxes player.vision-hitboxes)
(set self.player-vision-pts player.vision-pts)))
(let [previousCanvas (love.graphics.getCanvas)]
(love.graphics.setCanvas self.canvas)
@ -24,26 +31,35 @@
(love.graphics.setCanvas previousCanvas)
))
(lambda fow.draw9 [self]
(let [
previousCanvas (love.graphics.getCanvas)
previousBlendMode (love.graphics.getBlendMode)
[x y] self.player-pos]
(love.graphics.setCanvas self.canvas)
(love.graphics.clear)
(color.set-color :dark-purple)
(love.graphics.rectangle "fill" 0 0 1000 1000)
(lambda fow.draw10 [self]
(set self.main-canvas (love.graphics.getCanvas))
(love.graphics.setCanvas self.canvas)
(love.graphics.clear)
(color.set-color :dark-purple)
(love.graphics.rectangle "fill" 0 0 1000 1000))
(lambda fow.draw15 [self]
(let [previousBlendMode (love.graphics.getBlendMode)]
(love.graphics.setBlendMode "replace")
(love.graphics.setColor 0 0 0 0) ; transparent
(love.graphics.push)
(love.graphics.translate (- x 3) (- y 7))
(love.graphics.translate (unpack self.player-pt))
(love.graphics.translate -3 -7)
(love.graphics.polygon "fill" 3 11 3 17 7 21 17 21 21 17 21 11 17 7 7 7)
(love.graphics.pop)
; draw the vision cone
(when (> (length self.player-vision-pts) 2)
(love.graphics.polygon "fill" self.player-vision-pts))
(love.graphics.setBlendMode previousBlendMode)
(love.graphics.setCanvas previousCanvas)))
(when (> (length self.player-vision-pts) 2)
(love.graphics.polygon "fill" self.player-vision-pts))
; draw the vision hitboxes
(each [_ hitbox (pairs self.player-vision-hitboxes)]
(love.graphics.rectangle "fill" hitbox.x hitbox.y hitbox.w hitbox.h))
(love.graphics.setBlendMode previousBlendMode))
(color.set-color :white)
(each [_ hitbox (pairs self.player-vision-hitboxes)]
(love.graphics.rectangle "line" hitbox.x hitbox.y hitbox.w hitbox.h))
(love.graphics.setCanvas self.main-canvas))
(lambda fow.draw80 [self]
(color.reset-color)

View file

@ -8,6 +8,9 @@
{ :x 0
:y 0
:rot 0
:debug-pt [0 0]
:boxes {}
:size 150
:speed 4 })
(lambda radar.load [self]
@ -16,18 +19,46 @@
(set self.x x)
(set self.y y)))))
(lambda vision-bump-filter [other]
(= other.behavior "block"))
(lambda radar.update [self dt]
(set self.rot (+ self.rot (* (/ dt self.speed)))))
(set self.rot (+ self.rot (* (/ dt self.speed))))
; if we are over the limit of radar boxes the oldest out
(let [
ox self.x
oy self.y
x ox
y (- oy self.size)
(tip-x tip-y) (utils.rotate-pt x y ox oy (+ self.rot utils.deg-90))
items (self.pool.data.bump-world:querySegment ox oy tip-x tip-y vision-bump-filter)]
(when (> (length items) 0)
(each [_ item (ipairs items)]
(set item.lase-seen dt)
(tset self.boxes item item)))
(set self.debug-pt [tip-x tip-y])))
(lambda radar.draw11 [self]
"Draw radar boxes"
(color.set-color :light-pink)
(each [_ box (pairs self.boxes)]
(love.graphics.rectangle "line" box.x box.y box.w box.h)
(love.graphics.line box.x box.y (+ box.x box.w) (+ box.y box.h))
(love.graphics.line (+ box.x box.w) box.y box.x (+ box.y box.h))))
(lambda radar.draw81 [self]
(color.set-color :dark-pink)
(love.graphics.setPointSize 4)
(love.graphics.points (unpack self.debug-pt))
(love.graphics.line self.x self.y (. self.debug-pt 1) (. self.debug-pt 2))
(love.graphics.push)
(love.graphics.translate self.x self.y)
(love.graphics.rotate self.rot)
(love.graphics.setPointSize 2)
(love.graphics.points 0 0)
(love.graphics.circle "line" 0 0 150)
(love.graphics.line 0 0 0 150)
(love.graphics.circle "line" 0 0 self.size)
(love.graphics.line 0 0 0 self.size)
(love.graphics.pop))
radar

View file

@ -30,6 +30,9 @@
(love.graphics.line x1 y1 x1 (+ y1 1000))
(love.graphics.print (.. (string.format "%d" x1) "," (string.format "%d" y1)) (+ x1 3) (+ y1 3)))))))
; (lambda gen-wall-collider-box [x y width height]
; )
(fn walls.load [self]
(set self.batch (love.graphics.newSpriteBatch assets.walls-sprite 2500))
;; load quads
@ -37,8 +40,8 @@
(for [i 0 19 1]
(table.insert self.quads (love.graphics.newQuad (* i 25) 0 25 25 w h))))
;; fill batch
(each [_ row (pairs (. levels :levels self.pool.data.level-key :tiles))]
(each [_ tile (pairs row)]
(each [row-key row (pairs (. levels :levels self.pool.data.level-key :tiles))]
(each [tile-key tile (pairs row)]
(let [
x tile.x
y tile.y
@ -47,12 +50,12 @@
(if (and (> id 0) (< id 21)) ;; 1-20 are wall tiles
(do
(self.batch:add (. self.quads id) (if tile.h-flip (+ x 25) x) y 0 (if tile.h-flip -1 1) 1)
(each [_ collider (pairs colliders)]
(each [collider-key collider (pairs colliders)]
(let [
mirrored-collider (if tile.h-flip (mirror-collider collider) collider)
collider-x (+ x mirrored-collider.x)
collider-y (+ y mirrored-collider.y)]
(self.pool.data.bump-world:add {: x : y :name :wall :behavior :block} collider-x collider-y mirrored-collider.width mirrored-collider.height)
(self.pool.data.bump-world:add {:id (.. row-key tile-key collider-key) : x : y :name :wall :behavior :block :w mirrored-collider.width :h mirrored-collider.height} collider-x collider-y mirrored-collider.width mirrored-collider.height)
(table.insert
self.collider-debug-boxes
{:x collider-x :y collider-y :width mirrored-collider.width :height mirrored-collider.height}))))))))
@ -65,7 +68,7 @@
)
(fn walls.draw49 [self]
(fn walls.draw40 [self]
(color.reset-color)
(love.graphics.draw self.batch))