Created
July 18, 2025 16:07
-
-
Save RikusLategan/eb1f26a4fd1add43ce11e34cce1a70a2 to your computer and use it in GitHub Desktop.
This is the same demo using two different 2d go ported physic engines ( #Chipmunk and #Box2d )
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "image/color" | |
| "log" | |
| "math/rand" | |
| "github.com/hajimehoshi/ebiten/v2" | |
| ebitvector "github.com/hajimehoshi/ebiten/v2/vector" | |
| "github.com/ByteArena/box2d" | |
| ) | |
| // entity represents a ball in the Box2D world. | |
| type entity struct { | |
| body *box2d.B2Body | |
| radius float64 | |
| colour color.RGBA | |
| } | |
| // Game holds our entities and the Box2D world. | |
| type Game struct { | |
| entities []entity | |
| world *box2d.B2World | |
| dt float64 | |
| } | |
| // init sets up Box2D, creates dynamic balls, and static walls. | |
| func (g *Game) init() { | |
| g.dt = 1.0 / 60.0 | |
| // Create Box2D world with gravity pointing down | |
| gravity := box2d.MakeB2Vec2(0, 1000) | |
| g.world = box2d.MakeB2World(gravity) | |
| // Create 14 balls at random positions/velocities | |
| g.entities = make([]entity, 14) | |
| for i := range g.entities { | |
| x := float64(rand.Intn(640)) | |
| y := float64(rand.Intn(480)) | |
| radius := 5.0 | |
| // Define dynamic body | |
| bd := box2d.NewB2BodyDef() | |
| bd.Type = box2d.B2BodyType.B2DynamicBody | |
| bd.Position = box2d.MakeB2Vec2(x, y) | |
| body := g.world.CreateBody(bd) | |
| // Attach a circle fixture | |
| circle := box2d.NewB2CircleShape() | |
| circle.M_radius = radius | |
| fd := box2d.NewB2FixtureDef() | |
| fd.Shape = circle | |
| fd.Density = 1.0 | |
| fd.Friction = 0.7 | |
| fd.Restitution = 0.9 | |
| body.CreateFixtureFromDef(fd) | |
| // Give it an initial random velocity | |
| vx := -50 + rand.Float64()*100 | |
| vy := -50 + rand.Float64()*100 | |
| body.SetLinearVelocity(box2d.MakeB2Vec2(vx, vy)) | |
| g.entities[i] = entity{ | |
| body: body, | |
| radius: radius, | |
| colour: color.RGBA{ | |
| uint8(rand.Intn(256)), | |
| uint8(rand.Intn(256)), | |
| uint8(rand.Intn(256)), | |
| 0xff, | |
| }, | |
| } | |
| } | |
| // Helper to create a static edge (wall) | |
| makeWall := func(x1, y1, x2, y2 float64) { | |
| bd := box2d.NewB2BodyDef() | |
| bd.Type = box2d.B2BodyType.B2StaticBody | |
| body := g.world.CreateBody(bd) | |
| edge := box2d.NewB2EdgeShape() | |
| edge.Set(box2d.MakeB2Vec2(x1, y1), box2d.MakeB2Vec2(x2, y2)) | |
| fd := box2d.NewB2FixtureDef() | |
| fd.Shape = edge | |
| fd.Friction = 0.7 | |
| fd.Restitution = 1.0 | |
| body.CreateFixtureFromDef(fd) | |
| } | |
| screenW, screenH := 640.0, 480.0 | |
| wallThickness := 10.0 | |
| // Left | |
| makeWall(0, 0, 0, screenH) | |
| // Right | |
| makeWall(screenW, 0, screenW, screenH) | |
| // Top | |
| makeWall(0, 0, screenW, 0) | |
| // Bottom | |
| makeWall(0, screenH, screenW, screenH) | |
| } | |
| // Update steps the Box2D world. | |
| func (g *Game) Update() error { | |
| velIter, posIter := 8, 3 | |
| g.world.Step(g.dt, velIter, posIter) | |
| return nil | |
| } | |
| // Draw renders each ball as a filled circle. | |
| func (g *Game) Draw(screen *ebiten.Image) { | |
| screen.Fill(color.RGBA{0x10, 0, 0, 0xff}) | |
| for _, e := range g.entities { | |
| pos := e.body.GetPosition() | |
| ebitvector.DrawFilledCircle( | |
| screen, | |
| float32(pos.X), | |
| float32(pos.Y), | |
| float32(e.radius), | |
| e.colour, | |
| true, | |
| ) | |
| } | |
| } | |
| // Layout defines the Ebiten window size. | |
| func (g *Game) Layout(w, h int) (int, int) { | |
| return 640, 480 | |
| } | |
| func main() { | |
| ebiten.SetWindowSize(640, 480) | |
| ebiten.SetWindowTitle("Gogolf (Box2D)") | |
| game := &Game{} | |
| game.init() | |
| if err := ebiten.RunGame(game); err != nil { | |
| log.Fatal(err) | |
| } | |
| fmt.Println(game.Layout(0, 0)) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "image/color" | |
| "log" | |
| "math/rand" | |
| "github.com/hajimehoshi/ebiten/v2" | |
| ebitvector "github.com/hajimehoshi/ebiten/v2/vector" | |
| cp "github.com/jakecoffman/cp" | |
| ) | |
| // entity represents a ball or other object in the physics simulation. | |
| // Each entity has a physics body, a shape for collision, and a color for rendering. | |
| type entity struct { | |
| body *cp.Body // The physics body (position, velocity, etc.) | |
| shape *cp.Shape // The collision shape (circle, polygon, etc.) | |
| colour color.RGBA // The color used when drawing the entity | |
| } | |
| // Game holds all the state for our game, including entities, physics space, and the mountain platform. | |
| type Game struct { | |
| entities []entity // All dynamic entities (balls) in the game | |
| space *cp.Space // The Chipmunk2D physics simulation space | |
| dt float64 // The physics time step (seconds per frame) | |
| } | |
| // init sets up the physics world, creates balls, the mountain, and the walls. | |
| func (game *Game) init() { | |
| // Set the physics simulation time step to 1/60th of a second. | |
| game.dt = 1.0 / 60.0 | |
| // Create a new Chipmunk2D physics space and set gravity (downwards). | |
| game.space = cp.NewSpace() | |
| game.space.SetGravity(cp.Vector{X: 0, Y: 1000}) | |
| // --- Create balls --- | |
| // We'll create 14 balls with random positions, velocities, and colors. | |
| game.entities = make([]entity, 14) | |
| for i := 0; i < len(game.entities); i++ { | |
| x := float64(rand.Intn(640)) // Random X position within screen width | |
| y := float64(rand.Intn(480)) // Random Y position within screen height | |
| radius := 5.0 // Ball radius | |
| mass := 1.0 // Ball mass | |
| // Calculate moment of inertia for a circle | |
| moment := cp.MomentForCircle(mass, 0, radius, cp.Vector{}) | |
| // Create the physics body and set its position and velocity | |
| body := cp.NewBody(mass, moment) | |
| body.SetPosition(cp.Vector{X: x, Y: y}) | |
| body.SetVelocity(-50+rand.Float64()*100, -50+rand.Float64()*100) | |
| // Create the collision shape (circle) and set physics properties | |
| shape := cp.NewCircle(body, radius, cp.Vector{}) | |
| shape.SetElasticity(0.9) // Perfectly bouncy | |
| shape.SetFriction(0.7) // Some friction | |
| // Add the body and shape to the physics space | |
| game.space.AddBody(body) | |
| game.space.AddShape(shape) | |
| // Assign a random color to each ball | |
| game.entities[i] = entity{ | |
| body: body, | |
| shape: shape, | |
| colour: color.RGBA{uint8(rand.Intn(256)), uint8(rand.Intn(256)), uint8(rand.Intn(256)), 0xff}, | |
| } | |
| } | |
| // ...existing code... | |
| // --- Create walls --- | |
| wallThickness := 10.0 | |
| screenW, screenH := 640.0, 480.0 | |
| // Left wall | |
| leftBody := cp.NewStaticBody() | |
| leftShape := cp.NewSegment(leftBody, cp.Vector{X: 0, Y: 0}, cp.Vector{X: 0, Y: screenH}, wallThickness) | |
| leftShape.SetElasticity(1.0) | |
| leftShape.SetFriction(0.7) | |
| game.space.AddShape(leftShape) | |
| // Rig | |
| rightBody := cp.NewStaticBody() | |
| rightShape := cp.NewSegment(rightBody, cp.Vector{X: screenW, Y: 0}, cp.Vector{X: screenW, Y: screenH}, wallThickness) | |
| rightShape.SetElasticity(1.0) | |
| rightShape.SetFriction(0.7) | |
| game.space.AddShape(rightShape) | |
| // T | |
| topBody := cp.NewStaticBody() | |
| topShape := cp.NewSegment(topBody, cp.Vector{X: 0, Y: 0}, cp.Vector{X: screenW, Y: 0}, wallThickness) | |
| topShape.SetElasticity(1.0) | |
| topShape.SetFriction(0.7) | |
| game.space.AddShape(topShape) | |
| ebitvector.DrawFilledPath() | |
| // Bott | |
| bottomBody := cp.NewStaticBody() | |
| bottomShape := cp.NewSegment(bottomBody, cp.Vector{X: 0, Y: screenH}, cp.Vector{X: screenW, Y: screenH}, wallThickness) | |
| bottomShape.SetElasticity(1.0) | |
| bottomShape.SetFriction(0.7) | |
| game.space.AddShape(bottomShape) | |
| } | |
| // Update advances the physics simulation by one time step. | |
| // All collisions and physics are handled automatically by Chipmunk2D. | |
| func (game *Game) Update() error { | |
| game.space.Step(game.dt) | |
| return nil | |
| } | |
| // Draw renders the game scene using Ebiten. | |
| // Balls are drawn as filled circles, and the mountain is drawn as a polygon. | |
| func (game *Game) Draw(screen *ebiten.Image) { | |
| // Fill the background with a dark color | |
| screen.Fill(color.RGBA{0x10, 0, 0, 0xff}) | |
| // Draw each ball | |
| for _, entity := range game.entities { | |
| pos := entity.body.Position() | |
| ebitvector.DrawFilledCircle(screen, float32(pos.X), float32(pos.Y), 5, entity.colour, true) | |
| } | |
| } | |
| // Layout specifies the window size for Ebiten. | |
| func (game *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { | |
| return 640, 480 | |
| } | |
| // main is the entry point of the program. | |
| // It sets up the window, initializes the game, and starts the game loop. | |
| func main() { | |
| ebiten.SetWindowSize(640, 480) | |
| ebiten.SetWindowTitle("Gogolf") | |
| game := &Game{} | |
| game.init() | |
| if err := ebiten.RunGame(game); err != nil { | |
| log.Fatal(err) | |
| } | |
| fmt.Println(game.Layout(0, 0)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment