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.

Breezy greens, running as a native Yeho application on Windows
Actual native application capture. The downloadable source produces this example.

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.

model.yh
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."
    }
}