DEV Community

Cover image for Mastering TCJSGame Movement Utilities: Beyond Basic Motion
Kehinde Owolabi
Kehinde Owolabi

Posted on

Mastering TCJSGame Movement Utilities: Beyond Basic Motion

Mastering TCJSGame Movement Utilities: Beyond Basic Motion

Game Movement Mechanics

Movement is the soul of any great game. While basic speedX and speedY properties can get you started, TCJSGame's movement utilities unlock a world of smooth, professional game mechanics that will make your games feel polished and responsive.

In this deep dive, we'll explore the powerful move object and transform how you think about character movement, physics, and game feel.

Why Advanced Movement Matters

Before we dive into code, let's understand why movement utilities are game-changers:

  • Consistent Experience: Frame-rate independent movement across all devices
  • Professional Polish: Smooth animations and transitions that feel premium
  • Reduced Code: Complex movements become one-line function calls
  • Better Game Feel: Responsive controls that keep players engaged

🎯 The Movement Utility Toolbox

TCJSGame's move object provides these essential functions:

// Basic positioning
move.teleport(), move.setX(), move.setY(), move.position()

// Smooth movement  
move.glideX(), move.glideY(), move.glideTo()

// Physics and advanced motion
move.project(), move.circle(), move.pointTo()

// Acceleration and control
move.accelerate(), move.decelerate()

// Boundaries and constraints
move.bound(), move.boundTo()
Enter fullscreen mode Exit fullscreen mode

Let's master each category with practical examples.

🚀 Smooth Transitions with Glide Functions

Basic Glide Movement

const player = new Component(30, 30, "blue", 100, 100, "rect");
display.add(player);

// Glide horizontally over 2 seconds
move.glideX(player, 2, 400);

// Glide vertically over 1.5 seconds  
move.glideY(player, 1.5, 300);

// Glide to specific coordinates
move.glideTo(player, 2, 400, 300);
Enter fullscreen mode Exit fullscreen mode

Advanced Glide Patterns

// Platformer checkpoint system
function moveToCheckpoint() {
    move.glideTo(player, 1.5, checkpoint.x, checkpoint.y);

    // Add callback for arrival
    setTimeout(() => {
        console.log("Checkpoint reached!");
        showCheckpointEffect();
    }, 1500);
}

// Menu navigation system
const menuItems = [item1, item2, item3];
let selectedIndex = 0;

function navigateMenu(direction) {
    selectedIndex = (selectedIndex + direction + menuItems.length) % menuItems.length;

    // Smooth selection movement
    menuItems.forEach((item, index) => {
        const targetY = 100 + (index - selectedIndex) * 80;
        move.glideY(item, 0.3, targetY);

        // Scale effect for selected item
        const targetScale = index === selectedIndex ? 1.2 : 1.0;
        move.glideTo(item, 0.3, item.x, targetY); // Combined positioning
    });
}
Enter fullscreen mode Exit fullscreen mode

🎯 Physics-Based Movement

Projectile Motion Made Simple

// Basic projectile
function throwBall(angle, power) {
    move.project(ball, power, angle, 0.2);
}

// Angled jump for platformer
function diagonalJump() {
    move.project(player, 12, 45, 0.5); // 45-degree jump
}

// Basketball shooting game
function shootBasketball() {
    const angle = calculateShotAngle(player, basket);
    const power = calculateShotPower(distance);
    move.project(basketball, power, angle, 0.3);
}

// Grenade throwing with arc
function throwGrenade(targetX, targetY) {
    const dx = targetX - player.x;
    const dy = targetY - player.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    const angle = Math.atan2(dy, dx) * (180 / Math.PI);

    move.project(grenade, distance * 0.1, angle, 0.4);
}
Enter fullscreen mode Exit fullscreen mode

Circular and Orbital Motion

// Planetary orbits
const planets = {
    mercury: new Component(10, 10, "gray", 200, 200, "rect"),
    venus: new Component(15, 15, "orange", 250, 200, "rect"),
    earth: new Component(16, 16, "blue", 300, 200, "rect")
};

// Set up orbits with different speeds
move.circle(planets.mercury, 3);    // Fast orbit
move.circle(planets.venus, 1.5);    // Medium orbit  
move.circle(planets.earth, 1);      // Slow orbit

// Moon orbiting earth
function updateMoonOrbit() {
    const orbitRadius = 50;
    const orbitSpeed = 0.05;

    moon.x = earth.x + Math.cos(display.frameNo * orbitSpeed) * orbitRadius;
    moon.y = earth.y + Math.sin(display.frameNo * orbitSpeed) * orbitRadius;
}
Enter fullscreen mode Exit fullscreen mode

⚡ Acceleration and Advanced Control

Smooth Acceleration System

class AdvancedCharacter {
    constructor(component) {
        this.component = component;
        this.maxSpeed = 8;
        this.acceleration = 0.5;
        this.deceleration = 0.3;
        this.currentSpeedX = 0;
        this.currentSpeedY = 0;
    }

    update() {
        // Horizontal movement
        if (display.keys[37]) { // Left
            this.currentSpeedX = Math.max(this.currentSpeedX - this.acceleration, -this.maxSpeed);
        } else if (display.keys[39]) { // Right  
            this.currentSpeedX = Math.min(this.currentSpeedX + this.acceleration, this.maxSpeed);
        } else {
            // Deceleration
            if (this.currentSpeedX > 0) {
                this.currentSpeedX = Math.max(0, this.currentSpeedX - this.deceleration);
            } else if (this.currentSpeedX < 0) {
                this.currentSpeedX = Math.min(0, this.currentSpeedX + this.deceleration);
            }
        }

        // Apply movement
        this.component.speedX = this.currentSpeedX;
        this.component.speedY = this.currentSpeedY;
    }
}

// Usage
const playerController = new AdvancedCharacter(player);
Enter fullscreen mode Exit fullscreen mode

Built-in Acceleration Utilities

// Using move.accelerate() and move.decelerate()
function updatePlayerMovement() {
    // Acceleration with max speed limits
    if (display.keys[37]) {
        move.accelerate(player, -0.5, 0, 8); // Left acceleration
    } else if (display.keys[39]) {
        move.accelerate(player, 0.5, 0, 8);  // Right acceleration
    } else {
        move.decelerate(player, 0.3, 0);      // Automatic deceleration
    }

    // Vertical movement (flying/jumping)
    if (display.keys[38]) {
        move.accelerate(player, 0, -0.4, 6); // Up acceleration
    } else if (display.keys[40]) {
        move.accelerate(player, 0, 0.4, 6);  // Down acceleration  
    } else {
        move.decelerate(player, 0, 0.2);     // Vertical deceleration
    }
}
Enter fullscreen mode Exit fullscreen mode

🎮 Real-World Game Examples

Complete Platformer Movement System

class PlatformerMovement {
    constructor(player) {
        this.player = player;
        this.groundSpeed = 6;
        this.airSpeed = 4;
        this.jumpPower = 12;
        this.isGrounded = false;
    }

    update() {
        // Get movement input
        let moveInput = 0;
        if (display.keys[37]) moveInput = -1; // Left
        if (display.keys[39]) moveInput = 1;  // Right

        // Apply movement with ground/air difference
        const currentSpeed = this.isGrounded ? this.groundSpeed : this.airSpeed;
        this.player.speedX = moveInput * currentSpeed;

        // Jumping
        if ((display.keys[38] || display.keys[32]) && this.isGrounded) {
            this.player.speedY = -this.jumpPower;
            this.isGrounded = false;
        }

        // Air control (reduced in air)
        if (!this.isGrounded) {
            this.player.speedX *= 0.95; // Air resistance
        }
    }

    land() {
        this.isGrounded = true;
        this.player.speedY = 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Top-Down Shooter Movement

class TopDownMovement {
    constructor(ship) {
        this.ship = ship;
        this.acceleration = 0.2;
        this.maxSpeed = 6;
        this.rotationSpeed = 0.1;
    }

    update() {
        // Forward/backward movement (W/S keys)
        if (display.keys[87]) { // W - Forward
            move.accelerate(this.ship, 
                Math.cos(this.ship.angle) * this.acceleration,
                Math.sin(this.ship.angle) * this.acceleration,
                this.maxSpeed
            );
        }
        if (display.keys[83]) { // S - Backward
            move.accelerate(this.ship,
                -Math.cos(this.ship.angle) * this.acceleration * 0.5,
                -Math.sin(this.ship.angle) * this.acceleration * 0.5,
                this.maxSpeed * 0.5
            );
        }

        // Rotation (A/D keys)
        if (display.keys[65]) { // A - Rotate left
            this.ship.angle -= this.rotationSpeed;
        }
        if (display.keys[68]) { // D - Rotate right
            this.ship.angle += this.rotationSpeed;
        }

        // Automatic deceleration (space friction)
        move.decelerate(this.ship, 0.05, 0.05);

        // Screen wrapping
        this.wrapAroundScreen();
    }

    wrapAroundScreen() {
        if (this.ship.x < -this.ship.width) this.ship.x = display.canvas.width;
        if (this.ship.x > display.canvas.width) this.ship.x = -this.ship.width;
        if (this.ship.y < -this.ship.height) this.ship.y = display.canvas.height;
        if (this.ship.y > display.canvas.height) this.ship.y = -this.ship.height;
    }
}
Enter fullscreen mode Exit fullscreen mode

Smart Enemy AI Movement

class EnemyAI {
    constructor(enemy, player) {
        this.enemy = enemy;
        this.player = player;
        this.states = {
            PATROL: 'patrol',
            CHASE: 'chase',
            ATTACK: 'attack',
            FLEE: 'flee'
        };
        this.currentState = this.states.PATROL;
        this.patrolPoints = [{x: 100, y: 100}, {x: 400, y: 100}, {x: 400, y: 300}];
        this.currentPatrolIndex = 0;
    }

    update() {
        const distanceToPlayer = state.distance(this.enemy, this.player);

        // State transitions
        if (distanceToPlayer < 150 && this.currentState !== this.states.CHASE) {
            this.currentState = this.states.CHASE;
        } else if (distanceToPlayer > 300 && this.currentState === this.states.CHASE) {
            this.currentState = this.states.PATROL;
        } else if (distanceToPlayer < 50) {
            this.currentState = this.states.ATTACK;
        }

        // State behaviors
        switch(this.currentState) {
            case this.states.PATROL:
                this.patrolBehavior();
                break;
            case this.states.CHASE:
                this.chaseBehavior();
                break;
            case this.states.ATTACK:
                this.attackBehavior();
                break;
        }
    }

    patrolBehavior() {
        const target = this.patrolPoints[this.currentPatrolIndex];
        const distance = state.distance(this.enemy, target);

        // Point toward patrol point
        move.pointTo(this.enemy, target.x, target.y);

        // Move toward target
        move.accelerate(this.enemy, 0.1, 0, 2);

        // Switch to next patrol point when close
        if (distance < 20) {
            this.currentPatrolIndex = (this.currentPatrolIndex + 1) % this.patrolPoints.length;
        }
    }

    chaseBehavior() {
        // Point toward player
        move.pointTo(this.enemy, this.player.x, this.player.y);

        // Chase with moderate speed
        move.accelerate(this.enemy, 0.2, 0, 4);
    }

    attackBehavior() {
        // Circle around player while attacking
        move.circle(this.enemy, 2);
        move.accelerate(this.enemy, 0.3, 0, 3);

        // Occasionally lunge toward player
        if (display.frameNo % 120 === 0) {
            move.pointTo(this.enemy, this.player.x, this.player.y);
            move.accelerate(this.enemy, 2, 0, 8);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🛡️ Boundary and Constraint Systems

Smart Boundary Management

// Flexible boundary system
class BoundaryManager {
    constructor() {
        this.boundaries = {
            left: 0,
            right: display.canvas.width,
            top: 0,
            bottom: display.canvas.height
        };
    }

    // Set custom boundaries for different game areas
    setLevelBoundaries(level) {
        switch(level) {
            case 'forest':
                this.boundaries = { left: 0, right: 2000, top: 0, bottom: 600 };
                break;
            case 'cave':
                this.boundaries = { left: 0, right: 800, top: 0, bottom: 400 };
                break;
            case 'sky':
                this.boundaries = { left: 0, right: 1200, top: -200, bottom: 800 };
                break;
        }
    }

    // Apply boundaries to multiple objects
    applyBoundaries(objects) {
        objects.forEach(obj => {
            move.boundTo(
                obj,
                this.boundaries.left,
                this.boundaries.right - obj.width,
                this.boundaries.top, 
                this.boundaries.bottom - obj.height
            );
        });
    }
}

// Usage
const boundaries = new BoundaryManager();
boundaries.setLevelBoundaries('forest');

function update() {
    boundaries.applyBoundaries([player, enemy1, enemy2]);
}
Enter fullscreen mode Exit fullscreen mode

Camera-Aware Boundary System

class CameraBoundarySystem {
    constructor(camera) {
        this.camera = camera;
    }

    updateObjectBoundaries(object) {
        // Calculate screen-relative boundaries
        const screenLeft = this.camera.x;
        const screenRight = this.camera.x + display.canvas.width;
        const screenTop = this.camera.y;
        const screenBottom = this.camera.y + display.canvas.height;

        // Apply boundaries with some margin
        const margin = 100;
        move.boundTo(
            object,
            screenLeft - margin,
            screenRight + margin - object.width,
            screenTop - margin,
            screenBottom + margin - object.height
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

⚡ Performance Optimization

Efficient Movement Updates

class OptimizedMovementSystem {
    constructor() {
        this.movingObjects = new Map();
        this.updateInterval = 2; // Update every 2 frames
    }

    addObject(object, movementType, config) {
        this.movingObjects.set(object, {
            type: movementType,
            config: config,
            lastUpdate: 0
        });
    }

    update() {
        this.movingObjects.forEach((data, object) => {
            // Only update objects that need movement this frame
            if (display.frameNo - data.lastUpdate >= this.updateInterval) {
                this.applyMovement(object, data);
                data.lastUpdate = display.frameNo;
            }
        });
    }

    applyMovement(object, data) {
        switch(data.type) {
            case 'patrol':
                this.patrolMovement(object, data.config);
                break;
            case 'orbit':
                this.orbitMovement(object, data.config);
                break;
            case 'follow':
                this.followMovement(object, data.config);
                break;
        }
    }

    patrolMovement(object, config) {
        // Efficient patrol logic
        const target = config.points[config.currentPoint];
        if (state.distance(object, target) < config.threshold) {
            config.currentPoint = (config.currentPoint + 1) % config.points.length;
        } else {
            move.pointTo(object, target.x, target.y);
            move.accelerate(object, 0.1, 0, config.speed);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🎯 Pro Tips and Best Practices

1. Movement Consistency Across Devices

// Always use delta time for speed calculations
function update(dt) {
    // Good: Frame-rate independent
    player.x += movementSpeed * dt;

    // Bad: Frame-rate dependent  
    // player.x += 5; // This will vary with FPS
}
Enter fullscreen mode Exit fullscreen mode

2. Layered Movement Systems

// Combine multiple movement types
function updateComplexMovement() {
    // Base movement
    move.accelerate(enemy, baseAccelX, baseAccelY, baseMaxSpeed);

    // Add wandering behavior
    const wanderX = Math.sin(display.frameNo * 0.01) * 0.1;
    const wanderY = Math.cos(display.frameNo * 0.015) * 0.1;
    move.accelerate(enemy, wanderX, wanderY, wanderMaxSpeed);

    // Apply player avoidance
    if (state.distance(enemy, player) < avoidDistance) {
        const avoidX = (enemy.x - player.x) * 0.001;
        const avoidY = (enemy.y - player.y) * 0.001;
        move.accelerate(enemy, avoidX, avoidY, avoidMaxSpeed);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Movement State Management

class MovementStateMachine {
    constructor(object) {
        this.object = object;
        this.states = new Map();
        this.currentState = null;
    }

    addState(name, enterFn, updateFn, exitFn) {
        this.states.set(name, { enterFn, updateFn, exitFn });
    }

    setState(newState) {
        if (this.currentState && this.states.get(this.currentState).exitFn) {
            this.states.get(this.currentState).exitFn();
        }

        this.currentState = newState;

        if (this.states.get(newState).enterFn) {
            this.states.get(newState).enterFn();
        }
    }

    update() {
        if (this.currentState && this.states.get(this.currentState).updateFn) {
            this.states.get(this.currentState).updateFn();
        }
    }
}

// Usage for character states
const playerMovement = new MovementStateMachine(player);

playerMovement.addState(
    'idle',
    () => { /* Stop movement, play idle animation */ },
    () => { /* Check for state transitions */ },
    () => { /* Clean up */ }
);

playerMovement.addState(
    'walking', 
    () => { /* Start walk animation */ },
    () => {
        // Walking movement logic
        if (display.keys[37]) move.accelerate(player, -0.3, 0, 4);
        if (display.keys[39]) move.accelerate(player, 0.3, 0, 4);
    },
    () => { /* Stop walk animation */ }
);
Enter fullscreen mode Exit fullscreen mode

🚀 Your Movement Challenge

Ready to master TCJSGame movement? Try these exercises:

  1. Create a smooth camera follow system that anticipates player movement
  2. Implement a grappling hook mechanic using projectile motion
  3. Build a racing game with realistic acceleration and drifting
  4. Create flocking behavior for groups of enemies or animals
  5. Implement a teleportation system with particle effects

📚 Conclusion

TCJSGame's movement utilities transform complex game mechanics into simple, powerful function calls. By mastering these tools, you can create:

  • Professional character controllers with smooth acceleration
  • Realistic physics systems without complex math
  • Smart AI behaviors that feel alive and responsive
  • Polished game feel that keeps players engaged

The key is to think beyond basic movement and leverage the full power of TCJSGame's utility functions. Combine them, layer them, and watch your games come alive with professional-quality motion.

What's the most interesting movement system you've created with TCJSGame? Share your creations and challenges in the comments below!


Ready for more? In our next article, we'll dive into TCJSGame's TileMap system and learn how to build complex, interactive game worlds. Follow to stay updated!

Top comments (0)