GAME / FREE / MIT EXAMPLE SOURCE
Breezy greens
Three greens and a beautiful breeze
Original cel-shaded island scenery, swaying grass, flowing wind, sand, water, and a playable three-hole round.

Take it for a spin.
While the ball is still, hold the left mouse button, aim toward a destination, and release. A longer aim gives more power. N advances after sinking a putt; R resets.
Extract the Windows download to a writable folder and open wind-golf.exe. Escape closes it. Database examples keep their local data in the working folder.
See how it works.
Dolphin math, frame-independent movement, friction, wind forces, terrain rules, and procedural scenery.
A small top-down golf game with illustrative physics and original artwork; no full 3D terrain engine.
READ / CHANGE / UNDERSTAND
The whole example is yours.
These are the exact files in the source download. Start with the model, then follow the window’s event loop. Comments explain the decisions.
using dolphin.math
// A small top-down golf model: velocity, friction, wind, sand, and a cup.
// The renderer decorates this exact state; it does not decide whether a shot lands.
class Golf
{
float x=240
float y=450
float vx=0
float vy=0
float safeX=240
float safeY=450
float holeX=735
float holeY=300
int strokes=0
int hole=1
bool sunk=false
bool roundComplete=false
text message="Aim for a gentle finish."
Reset()
{
this.x=240
this.y=450
this.vx=0
this.vy=0
this.safeX=this.x
this.safeY=this.y
this.holeX=735
this.holeY=300
this.strokes=0
this.hole=1
this.sunk=false
this.roundComplete=false
this.message="Aim for a gentle finish."
}
Speed() returns float
{
return DolphinLength(this.vx,this.vy)
}
Shoot(float aimX,float aimY)
{
if this.Speed()>3.0 or this.sunk or this.roundComplete
{
return
}
float dx=aimX-this.x
float dy=aimY-this.y
float distance=DolphinLength(dx,dy)
if distance<2.0
{
return
}
float power=Math.MinFloat(distance*1.65,420.0)
this.vx=dx/distance*power
this.vy=dy/distance*power
this.safeX=this.x
this.safeY=this.y
this.strokes=this.strokes+1
this.message="Let the breeze carry it."
}
Tick(float dt,float wind)
{
if this.sunk or this.roundComplete or this.Speed()<0.05
{
this.vx=0
this.vy=0
return
}
bool sand=DolphinDistance(this.x,this.y,542.0,388.0)<51.0
float drag=0.68
if sand
{
drag=2.9
}
this.vx=this.vx+(wind-this.vx*drag)*dt
this.vy=this.vy-this.vy*drag*dt
this.x=this.x+this.vx*dt
this.y=this.y+this.vy*dt
// A dry friction threshold lets the ball settle even in a persistent breeze.
if this.Speed()<6.0
{
this.vx=0
this.vy=0
}
float nx=(this.x-500.0)/431.0
float ny=(this.y-390.0)/213.0
if nx*nx+ny*ny>1.0
{
this.x=this.safeX
this.y=this.safeY
this.vx=0
this.vy=0
this.strokes=this.strokes+1
this.message="Into the water. Back to your lie (+1)."
}
if DolphinDistance(this.x,this.y,this.holeX,this.holeY)<13.0 and this.Speed()<85.0
{
this.x=this.holeX
this.y=this.holeY
this.vx=0
this.vy=0
this.sunk=true
this.message="In the cup! Press N for the next green."
if this.hole==3
{
this.roundComplete=true
this.message="Three greens complete. R starts a new round."
}
}
}
Next()
{
if !this.sunk or this.roundComplete
{
return
}
this.hole=this.hole+1
this.sunk=false
this.x=240
this.y=450
this.safeX=this.x
this.safeY=this.y
this.holeX=720
this.holeY=440
if this.hole==3
{
this.holeX=475
this.holeY=235
}
this.message="A fresh green. Read the wind."
}
}
