Vec2 Coordinate Systems
KAPLAY uses different coordinate systems to represent positions, directions, and offsets. The ReScript bindings provide distinct types for each coordinate system to ensure type safety and prevent coordinate system confusion.
Coordinate System Types
Section titled “Coordinate System Types”Vec2.World.t: Absolute positions in the game world (independent of camera/parent)Vec2.Local.t: Positions relative to a parent objectVec2.Screen.t: Positions relative to the camera/viewportVec2.Tile.t: Grid-based positions for tilemapsVec2.Unit.t: Direction vectors (not positions)
Creating Vectors
Section titled “Creating Vectors”// ✅ Use Context API methodslet worldPos = k->Context.vec2World(100., 200.)let localPos = k->Context.vec2Local(10., 5.)let screenPos = k->Context.vec2Screen(50., 75.)let direction = k->Context.vec2Up
// ❌ Don't construct records manuallylet badPos: Vec2.World.t = {x: 100., y: 200.}Coordinate Conversions
Section titled “Coordinate Conversions”// Screen ↔ Worldlet worldPos = k->Context.toWorld(screenPos)let screenPos = k->Context.toScreen(worldPos)
// Local ↔ World (via object transform)let worldPos = gameObj->GameObj.worldPoslet localPos = gameObj->GameObj.fromWorld(worldPos)
// Unit vectors (directions)let worldOffset = direction->Vec2.Unit.asWorld->Vec2.World.scaleWith(100.)Vector Operations
Section titled “Vector Operations”All coordinate types support the same operations (add, sub, scaleWith, addWithXY), but you must operate within the same coordinate system:
let pos1 = k->Context.vec2World(100., 200.)let pos2 = k->Context.vec2World(50., 75.)
let sum = pos1->Vec2.World.add(pos2)let offset = pos1->Vec2.World.addWithXY(10., 20.)Best Practices
Section titled “Best Practices”Use Vector Operations Instead of Manual Math
Section titled “Use Vector Operations Instead of Manual Math”// ✅ Goodlet newPos = lastPos->Vec2.World.addWithXY(deviation, direction.y)
// ❌ Badlet newPos: Vec2.World.t = { x: lastPos.x + deviation, y: lastPos.y + direction.y,}Convert at Boundaries
Section titled “Convert at Boundaries”Convert coordinate systems at the boundaries of your logic:
// Convert screen input to world for game logiclet worldPos = k->Context.toWorld(k->Context.mousePos)
// Convert world to local for drawinglet localPoints = worldPoints->Array.map(point => gameObj->GameObj.fromWorld(point))Store Coordinates in the Appropriate System
Section titled “Store Coordinates in the Appropriate System”// ✅ World positions for game logic (bounds checking, collisions)type t = {points: array<Vec2.World.t>}
// ✅ Local offsets for drawingtype t = {offset: Vec2.Local.t}Handle Deltas Correctly
Section titled “Handle Deltas Correctly”When computing deltas, convert positions first, then compute the difference:
// ✅ Good - convert first, then compute deltalet worldPos1 = k->Context.toWorld(screenPos1)let worldPos2 = k->Context.toWorld(screenPos2)let worldDelta = worldPos1->Vec2.World.sub(worldPos2)
// ❌ Bad - scaling doesn't convert coordinate systemslet screenDelta = screenPos1->Vec2.Screen.sub(screenPos2)// This is still in screen space!Common Patterns
Section titled “Common Patterns”Touch/Mouse Input
Section titled “Touch/Mouse Input”k->Context.onTouchStart((screenPos: Vec2.Screen.t, _touch) => { let worldPos = k->Context.toWorld(screenPos) // Use worldPos for game logic})Child Objects
Section titled “Child Objects”// Child uses local coordinateslet localPos = child->Child.getPoslet worldPos = child->Child.worldPoslet localFromWorld = child->Child.fromWorld(worldPos)Summary
Section titled “Summary”- Always use
ContextAPI methods to create vectors - Never construct Vec2 records manually
- Use vector operations (
addWithXY, etc.) instead of manual math - Convert coordinate systems at boundaries
- Store coordinates in the system that matches your use case