Mastering TCJSGame Movement Utilities: Beyond Basic Motion
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()
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);
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
});
}
🎯 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);
}
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;
}
⚡ 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);
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
}
}
🎮 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;
}
}
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;
}
}
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);
}
}
}
🛡️ 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]);
}
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
);
}
}
⚡ 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);
}
}
}
🎯 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
}
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);
}
}
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 */ }
);
🚀 Your Movement Challenge
Ready to master TCJSGame movement? Try these exercises:
- Create a smooth camera follow system that anticipates player movement
- Implement a grappling hook mechanic using projectile motion
- Build a racing game with realistic acceleration and drifting
- Create flocking behavior for groups of enemies or animals
- 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)