README.md
README.md
Failed to calculate interdiff for this file.
-33
coordinates/main.go
-33
coordinates/main.go
···
2
2
3
3
import (
4
4
"github.com/firefly-zero/firefly-go/firefly"
5
-
"github.com/orsinium-labs/tinymath"
6
5
)
7
6
8
7
var (
···
40
39
firefly.ClearScreen(firefly.ColorNone)
41
40
}
42
41
43
-
// draw 10 points in a circular way
44
-
ps := CirclePointsInt(10, 5)
45
-
for _, p := range ps {
46
-
firefly.DrawPoint(translatePoint(p), firefly.ColorRed)
47
-
}
48
-
49
42
// points in 4 quadrants
50
43
firefly.DrawPoint(translatePoint(firefly.Point{X: 10, Y: 10}), firefly.ColorCyan)
51
44
firefly.DrawPoint(translatePoint(firefly.Point{X: 10, Y: -10}), firefly.ColorGreen)
···
58
51
y := -p.Y + SCREENHEIGHT/2
59
52
// firefly.LogDebug(strings.Join([]string{"x: ", strconv.Itoa(x), ", y: ", strconv.Itoa(y)}, " "))
60
53
return firefly.Point{X: x, Y: y}
61
-
}
62
-
63
-
func CirclePointsInt(radius float32, n int) []firefly.Point {
64
-
points := make([]firefly.Point, 0, n)
65
-
seen := make(map[firefly.Point]struct{}, n)
66
-
67
-
// Wir samplen mit höherer Auflösung und sammeln eindeutige gerundete Punkte,
68
-
// bis wir n Stück haben.
69
-
// Oversample-Faktor dynamisch hochdrehen, falls nötig.
70
-
for oversample := n * 2; len(points) < n; oversample *= 2 {
71
-
for i := 0; i < oversample && len(points) < n; i++ {
72
-
angle := 2 * tinymath.Pi * float32(i) / float32(oversample)
73
-
74
-
x := int(tinymath.Round(radius * tinymath.Cos(angle)))
75
-
y := int(tinymath.Round(radius * tinymath.Sin(angle)))
76
-
77
-
p := firefly.Point{X: x, Y: y}
78
-
if _, ok := seen[p]; ok {
79
-
continue
80
-
}
81
-
seen[p] = struct{}{}
82
-
points = append(points, p)
83
-
}
84
-
}
85
-
86
-
return points
87
54
}
88
55
89
56
func drawCoordinateSystem() {
-11
tmp/go.mod
-11
tmp/go.mod
···
1
-
module github.com/voigt/firefly-zero-playground/tmp
2
-
3
-
go 1.25.0
4
-
5
-
require github.com/gen2brain/raylib-go/raylib v0.55.1
6
-
7
-
require (
8
-
github.com/ebitengine/purego v0.7.1 // indirect
9
-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
10
-
golang.org/x/sys v0.20.0 // indirect
11
-
)
-8
tmp/go.sum
-8
tmp/go.sum
···
1
-
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
2
-
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
3
-
github.com/gen2brain/raylib-go/raylib v0.55.1 h1:1rdc10WvvYjtj7qijHnV9T38/WuvlT6IIL+PaZ6cNA8=
4
-
github.com/gen2brain/raylib-go/raylib v0.55.1/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
5
-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
6
-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
7
-
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
8
-
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-196
tmp/main.go
-196
tmp/main.go
···
1
-
package main
2
-
3
-
import (
4
-
rl "github.com/gen2brain/raylib-go/raylib"
5
-
)
6
-
7
-
type Player struct {
8
-
Pos rl.Vector2
9
-
Vel rl.Vector2
10
-
Size rl.Vector2
11
-
OnGround bool
12
-
13
-
// Timers (seconds)
14
-
CoyoteTimer float32
15
-
JumpBuffer float32
16
-
Jumping bool // true after jump start until we land
17
-
}
18
-
19
-
func clamp(v, min, max float32) float32 {
20
-
if v < min {
21
-
return min
22
-
}
23
-
if v > max {
24
-
return max
25
-
}
26
-
return v
27
-
}
28
-
29
-
func approach(current, target, delta float32) float32 {
30
-
if current < target {
31
-
current += delta
32
-
if current > target {
33
-
return target
34
-
}
35
-
return current
36
-
}
37
-
if current > target {
38
-
current -= delta
39
-
if current < target {
40
-
return target
41
-
}
42
-
return current
43
-
}
44
-
return current
45
-
}
46
-
47
-
func main() {
48
-
const screenW, screenH int32 = 960, 540
49
-
rl.InitWindow(screenW, screenH, "raylib-go: platformer feel (accel/decel + buffer/coyote + variable jump + fast-fall)")
50
-
defer rl.CloseWindow()
51
-
rl.SetTargetFPS(60)
52
-
53
-
groundY := float32(screenH - 80)
54
-
groundThickness := float32(80)
55
-
56
-
p := Player{
57
-
Pos: rl.NewVector2(120, groundY-60),
58
-
Vel: rl.NewVector2(0, 0),
59
-
Size: rl.NewVector2(50, 60),
60
-
}
61
-
62
-
// ---------- Tuning ----------
63
-
maxRunSpeed := float32(380)
64
-
65
-
// Separate accel/decel
66
-
groundAccel := float32(2600)
67
-
groundDecel := float32(3200)
68
-
airAccel := float32(1400)
69
-
airDecel := float32(300)
70
-
71
-
// Jump
72
-
jumpSpeed := float32(640)
73
-
74
-
// Variable jump height: cut velocity when releasing jump while going up
75
-
jumpCutMultiplier := float32(0.35)
76
-
77
-
// Coyote time + jump buffer (seconds)
78
-
coyoteTime := float32(0.10)
79
-
jumpBufferTime := float32(0.12)
80
-
81
-
// --- Fast-fall tuning ---
82
-
// Base gravity is used when rising (Vel.Y < 0) and jump is held.
83
-
// When falling (Vel.Y > 0) we apply higher gravity (fallMultiplier).
84
-
// When jump is released early during rise, we apply higher gravity too (lowJumpMultiplier).
85
-
gravity := float32(1200) // "floatier" base on the way up
86
-
fallMultiplier := float32(2.2) // faster fall
87
-
lowJumpMultiplier := float32(2.6) // stronger pull-down if jump released early
88
-
maxFallSpeed := float32(2200) // higher terminal velocity for snappy fall
89
-
90
-
for !rl.WindowShouldClose() {
91
-
dt := rl.GetFrameTime()
92
-
93
-
// ---- Input ----
94
-
var dir float32 = 0
95
-
if rl.IsKeyDown(rl.KeyA) || rl.IsKeyDown(rl.KeyLeft) {
96
-
dir -= 1
97
-
}
98
-
if rl.IsKeyDown(rl.KeyD) || rl.IsKeyDown(rl.KeyRight) {
99
-
dir += 1
100
-
}
101
-
102
-
jumpPressed := rl.IsKeyPressed(rl.KeySpace)
103
-
jumpHeld := rl.IsKeyDown(rl.KeySpace)
104
-
jumpReleased := rl.IsKeyReleased(rl.KeySpace)
105
-
106
-
// ---- Timers ----
107
-
if p.OnGround {
108
-
p.CoyoteTimer = coyoteTime
109
-
} else {
110
-
p.CoyoteTimer = clamp(p.CoyoteTimer-dt, 0, 999)
111
-
}
112
-
113
-
if jumpPressed {
114
-
p.JumpBuffer = jumpBufferTime
115
-
} else {
116
-
p.JumpBuffer = clamp(p.JumpBuffer-dt, 0, 999)
117
-
}
118
-
119
-
// ---- Horizontal movement (accel/decel) ----
120
-
if p.OnGround {
121
-
if dir != 0 {
122
-
target := dir * maxRunSpeed
123
-
p.Vel.X = approach(p.Vel.X, target, groundAccel*dt)
124
-
} else {
125
-
p.Vel.X = approach(p.Vel.X, 0, groundDecel*dt)
126
-
}
127
-
} else {
128
-
if dir != 0 {
129
-
target := dir * maxRunSpeed
130
-
p.Vel.X = approach(p.Vel.X, target, airAccel*dt)
131
-
} else {
132
-
p.Vel.X = approach(p.Vel.X, 0, airDecel*dt)
133
-
}
134
-
}
135
-
p.Vel.X = clamp(p.Vel.X, -maxRunSpeed, maxRunSpeed)
136
-
137
-
// ---- Jump using buffer + coyote ----
138
-
if p.JumpBuffer > 0 && p.CoyoteTimer > 0 {
139
-
p.Vel.Y = -jumpSpeed
140
-
p.OnGround = false
141
-
p.CoyoteTimer = 0
142
-
p.JumpBuffer = 0
143
-
p.Jumping = true
144
-
}
145
-
146
-
// ---- Variable jump height (jump cut) ----
147
-
// Optional: keep it, feels good in addition to lowJumpMultiplier gravity.
148
-
if jumpReleased && p.Jumping && p.Vel.Y < 0 {
149
-
p.Vel.Y *= jumpCutMultiplier
150
-
}
151
-
152
-
// ---- Fast-fall gravity ----
153
-
// If falling: stronger gravity.
154
-
// If rising but jump not held: stronger gravity (low jump).
155
-
g := gravity
156
-
if p.Vel.Y > 0 {
157
-
g *= fallMultiplier
158
-
} else if p.Vel.Y < 0 && !jumpHeld {
159
-
g *= lowJumpMultiplier
160
-
}
161
-
p.Vel.Y += g * dt
162
-
p.Vel.Y = clamp(p.Vel.Y, -100000, maxFallSpeed)
163
-
164
-
// ---- Integrate ----
165
-
p.Pos.X += p.Vel.X * dt
166
-
p.Pos.Y += p.Vel.Y * dt
167
-
168
-
// ---- Ground collision ----
169
-
playerBottom := p.Pos.Y + p.Size.Y
170
-
if playerBottom >= groundY {
171
-
p.Pos.Y = groundY - p.Size.Y
172
-
p.Vel.Y = 0
173
-
p.OnGround = true
174
-
p.Jumping = false
175
-
} else {
176
-
p.OnGround = false
177
-
}
178
-
179
-
// ---- Screen bounds (X) ----
180
-
p.Pos.X = clamp(p.Pos.X, 0, float32(screenW)-p.Size.X)
181
-
if p.Pos.X == 0 || p.Pos.X == float32(screenW)-p.Size.X {
182
-
p.Vel.X = 0
183
-
}
184
-
185
-
// ---- Render ----
186
-
rl.BeginDrawing()
187
-
rl.ClearBackground(rl.RayWhite)
188
-
189
-
rl.DrawRectangle(0, int32(groundY), screenW, int32(groundThickness), rl.LightGray)
190
-
rl.DrawRectangleV(p.Pos, p.Size, rl.DarkBlue)
191
-
192
-
rl.DrawText("Move: A/D or Left/Right Jump: Space (buffer+coyote+variable+fast-fall)", 20, 20, 20, rl.Black)
193
-
194
-
rl.EndDrawing()
195
-
}
196
-
}
-47
touchpad/.zed/tasks.json
-47
touchpad/.zed/tasks.json
···
1
-
[
2
-
{
3
-
"label": "Build & Run Firefly Emulator",
4
-
"command": "firefly_cli build && firefly-emulator --id voigt.go-gamepad",
5
-
//"args": [],
6
-
// Env overrides for the command, will be appended to the terminal's environment from the settings.
7
-
// "env": { "foo": "bar" },
8
-
// Current working directory to spawn the command into, defaults to current project root.
9
-
//"cwd": "/path/to/working/directory",
10
-
// Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`.
11
-
"use_new_terminal": false,
12
-
// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`.
13
-
// "allow_concurrent_runs": false,
14
-
// What to do with the terminal pane and tab, after the command was started:
15
-
// * `always` — always show the task's pane, and focus the corresponding tab in it (default)
16
-
// * `no_focus` — always show the task's pane, add the task's tab in it, but don't focus it
17
-
// * `never` — do not alter focus, but still add/reuse the task's tab in its pane
18
-
"reveal": "always",
19
-
// What to do with the terminal pane and tab, after the command has finished:
20
-
// * `never` — Do nothing when the command finishes (default)
21
-
// * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it
22
-
// * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always`
23
-
"hide": "never",
24
-
// Which shell to use when running a task inside the terminal.
25
-
// May take 3 values:
26
-
// 1. (default) Use the system's default terminal configuration in /etc/passwd
27
-
// "shell": "system"
28
-
// 2. A program:
29
-
// "shell": {
30
-
// "program": "sh"
31
-
// }
32
-
// 3. A program with arguments:
33
-
// "shell": {
34
-
// "with_arguments": {
35
-
// "program": "/bin/bash",
36
-
// "args": ["--login"]
37
-
// }
38
-
// }
39
-
"shell": "system",
40
-
// Whether to show the task line in the output of the spawned task, defaults to `true`.
41
-
"show_summary": true,
42
-
// Whether to show the command line in the output of the spawned task, defaults to `true`.
43
-
"show_command": true,
44
-
// Represents the tags for inline runnable indicators, or spawning multiple tasks at once.
45
-
// "tags": []
46
-
},
47
-
]
-4
touchpad/firefly.toml
-4
touchpad/firefly.toml
-132
touchpad/gamepad.go
-132
touchpad/gamepad.go
···
1
-
package main
2
-
3
-
import ff "github.com/firefly-zero/firefly-go/firefly"
4
-
5
-
var (
6
-
point *ff.Point
7
-
center ff.Point
8
-
9
-
buttonN bool
10
-
buttonE bool
11
-
buttonS bool
12
-
buttonW bool
13
-
)
14
-
15
-
const (
16
-
radius = 26
17
-
third = ff.Width / 3
18
-
19
-
touchYcenter = third
20
-
touchXcenter = ff.Height/2 - radius/2
21
-
buttonYcenter = third * 2
22
-
buttonXcenter = ff.Height/2 - radius/2
23
-
)
24
-
25
-
func init() {
26
-
ff.Render = render
27
-
ff.Update = update
28
-
}
29
-
30
-
func update() {
31
-
// Buttons
32
-
buttons := ff.ReadButtons(ff.Combined)
33
-
34
-
if buttons.N {
35
-
buttonN = true
36
-
} else {
37
-
buttonN = false
38
-
}
39
-
if buttons.E {
40
-
buttonE = true
41
-
} else {
42
-
buttonE = false
43
-
}
44
-
if buttons.S {
45
-
buttonS = true
46
-
} else {
47
-
buttonS = false
48
-
}
49
-
if buttons.W {
50
-
buttonW = true
51
-
} else {
52
-
buttonW = false
53
-
}
54
-
55
-
// Touchpad
56
-
center = ff.Point{
57
-
X: touchXcenter,
58
-
Y: touchYcenter,
59
-
// X: ff.Width/2 - 45,
60
-
// Y: ff.Height / 2,
61
-
}
62
-
63
-
input, pressed := ff.ReadPad(ff.Combined)
64
-
if pressed {
65
-
point = &ff.Point{
66
-
X: center.X + input.X/20 - radius,
67
-
Y: center.Y - input.Y/20 - radius,
68
-
}
69
-
} else {
70
-
point = nil
71
-
}
72
-
}
73
-
74
-
func render() {
75
-
ff.ClearScreen(ff.ColorWhite)
76
-
77
-
// ff.DrawCircle(ff.Point{X: buttonYcenter - radius, Y: buttonXcenter - radius}, radius*3, ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1})
78
-
// ff.DrawCircle(ff.Point{X: touchYcenter - radius*2, Y: touchXcenter - radius}, radius*3, ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1})
79
-
ff.DrawLine(ff.Point{buttonYcenter, 0}, ff.Point{buttonYcenter, ff.Height}, ff.LineStyle{ff.ColorGray, 1})
80
-
ff.DrawLine(ff.Point{touchYcenter, 0}, ff.Point{touchYcenter, ff.Height}, ff.LineStyle{ff.ColorGray, 1})
81
-
ff.DrawLine(ff.Point{0, ff.Height / 2}, ff.Point{ff.Width, ff.Height / 2}, ff.LineStyle{ff.ColorGray, 1})
82
-
83
-
// Buttons
84
-
// B
85
-
styleE := ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
86
-
if buttonE {
87
-
styleE = ff.Style{FillColor: ff.ColorRed, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
88
-
}
89
-
ff.DrawCircle(ff.Point{X: buttonYcenter + radius, Y: buttonXcenter}, radius, styleE)
90
-
91
-
// A
92
-
styleS := ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
93
-
if buttonS {
94
-
styleS = ff.Style{FillColor: ff.ColorGreen, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
95
-
}
96
-
ff.DrawCircle(ff.Point{X: buttonYcenter, Y: buttonXcenter + radius}, radius, styleS)
97
-
98
-
// Y
99
-
styleN := ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
100
-
if buttonN {
101
-
styleN = ff.Style{FillColor: ff.ColorYellow, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
102
-
}
103
-
ff.DrawCircle(ff.Point{X: buttonYcenter, Y: buttonXcenter - radius}, radius, styleN)
104
-
105
-
// X
106
-
styleW := ff.Style{FillColor: ff.ColorWhite, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
107
-
if buttonW {
108
-
styleW = ff.Style{FillColor: ff.ColorBlue, StrokeColor: ff.ColorDarkBlue, StrokeWidth: 1}
109
-
}
110
-
ff.DrawCircle(ff.Point{X: buttonYcenter - radius, Y: buttonXcenter}, radius, styleW)
111
-
112
-
// Touchpad
113
-
style := ff.Style{
114
-
FillColor: ff.ColorWhite,
115
-
StrokeColor: ff.ColorDarkBlue,
116
-
StrokeWidth: 1,
117
-
}
118
-
119
-
ff.DrawCircle(ff.Point{
120
-
X: touchYcenter - radius*2,
121
-
Y: touchXcenter - radius,
122
-
}, radius*3, style)
123
-
124
-
style = ff.Style{
125
-
FillColor: ff.ColorBlue,
126
-
StrokeColor: ff.ColorDarkBlue,
127
-
StrokeWidth: 1,
128
-
}
129
-
if point != nil {
130
-
ff.DrawCircle(*point, radius, style)
131
-
}
132
-
}
-7
touchpad/go.mod
-7
touchpad/go.mod
-8
touchpad/go.sum
-8
touchpad/go.sum
···
1
-
github.com/firefly-zero/firefly-go v0.8.1 h1:evb25qj7ELM0wy20IIHtW8+4MI1i+wqVOiCsYdTuh3M=
2
-
github.com/firefly-zero/firefly-go v0.8.1/go.mod h1:+X/XGyPdES51OESkV8NSf1mszEBZionoROM7x2pBofw=
3
-
github.com/firefly-zero/firefly-go v0.9.5 h1:BzBr4t76bDVVFwNaWJixs1XasqLNBIdg0k9JXblBfU4=
4
-
github.com/firefly-zero/firefly-go v0.9.5/go.mod h1:+X/XGyPdES51OESkV8NSf1mszEBZionoROM7x2pBofw=
5
-
github.com/orsinium-labs/tinymath v1.0.0 h1:Uzp3GmjWIBxMObx4MQi9ACDu4Q8WKjSRakB1OMo9Bu0=
6
-
github.com/orsinium-labs/tinymath v1.0.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
7
-
github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTeV1vS5UA=
8
-
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
History
2 rounds
0 comments
voigt.tngl.sh
submitted
#1
expand 0 comments
closed without merging