Analog Stick
Read the N64 analog stick for smooth directional movement and aiming.
import gs "github.com/drpaneas/gosprite64"
StickPosition
StickPosition returns the analog stick's X and Y position as two float64 values, each in the range -1.0 to 1.0.
func StickPosition(deadzone float64) (float64, float64)
- X axis: -1.0 is full left, 1.0 is full right.
- Y axis: -1.0 is full up, 1.0 is full down. The Y axis is flipped compared to raw hardware values so that "down on the screen" is positive, matching screen coordinates.
- Values are clamped to [-1.0, 1.0] even if the hardware reports values slightly outside that range.
The deadzone parameter
N64 analog sticks rarely rest at a perfect zero position. A worn stick might report small non-zero values even when the player is not touching it. The deadzone parameter defines a threshold: any axis value between -deadzone and +deadzone is snapped to zero.
A deadzone of 0.15 is a good starting point for most games:
x, y := gs.StickPosition(0.15)
0.0- No deadzone. The raw (clamped) value is returned. Fine for menus or testing, but can cause drift on worn controllers.0.10 - 0.20- Typical range for action games. Filters out stick drift while preserving sensitivity.0.25+- Large deadzone. The player must push the stick further before movement registers. Useful for games where accidental input is costly.
Character movement example
const moveSpeed = 3.0
func (g *Game) Update() {
x, y := gs.StickPosition(0.15)
player.X += x * moveSpeed
player.Y += y * moveSpeed
}
Because StickPosition returns values between -1.0 and 1.0, multiplying by a speed constant gives smooth, proportional movement. Gentle stick tilts produce slow movement; full tilts produce maximum speed.
Eight-directional movement with normalization
When the stick is pushed diagonally, both X and Y are non-zero. Naively adding both axes makes diagonal movement ~41% faster than cardinal movement. Normalize the direction vector to fix this:
import "math"
func (g *Game) Update() {
x, y := gs.StickPosition(0.15)
length := math.Sqrt(x*x + y*y)
if length > 0 {
if length > 1.0 {
length = 1.0
}
player.X += (x / length) * moveSpeed * length
player.Y += (y / length) * moveSpeed * length
}
}
Aiming and cursor control
The analog stick works well for aiming a cursor or rotating a character:
func (g *Game) Update() {
x, y := gs.StickPosition(0.2)
if x != 0 || y != 0 {
player.AimAngle = math.Atan2(y, x)
}
}
Port-zero convenience
StickPosition reads from controller port 0. For multiplayer input, use PlayerStickPosition(port, deadzone) - see Multi-Controller Support.