How to create spin game
Creating the Map and Objects for Movement
Add a sphere object from the Mesh assets, then adjust its position and size as needed.
For the player object, create an empty object to enable movement via rotation.
Add a sphere to this empty object and adjust its position.
Rename the empty object to “player” and the sphere object to “body.”
You can add any asset and remove its child objects to use it as an empty object.
Attach the main camera to the player object so it follows the player’s movements.
Adjust the camera’s position and angle to ensure a clear view of the player.
Writing a Script for Player Movement Using Arrow Keys
If you started the project in Metaverse, delete all the code in the PresetScript first.
a. Use the OnKeyDown and OnKeyUp functions to handle keyboard input.
b. Rotate the player object based on the input data.
const player = WORLD.getObject("player");
const body = WORLD.getObject("body");
const key_state = { w: false, a: false, s: false, d: false }; // Array to store key input states
const speed = 30; // Player's speed
let x_rotate = speed;
let z_rotate = 0; // Initial setting to automatically move downward
function Update(dt) {
player.rotate(dt * x_rotate, 0, dt * z_rotate); // Adjust both x and z values simultaneously using the rotate function
}
function OnKeyDown(event) {
switch (event.code) {
case "KeyW":
case "ArrowUp":
x_rotate = -speed; // Move up
key_state.w = true;
if (!key_state.a && !key_state.d) {
z_rotate = 0; // Stop auto-rolling if applicable
}
break;
case "KeyS":
case "ArrowDown":
x_rotate = speed; // Move down
key_state.s = true;
if (!key_state.a && !key_state.d) {
z_rotate = 0; // Stop auto-rolling if applicable
}
break;
case "KeyA":
case "ArrowLeft":
z_rotate = speed; // Move left
key_state.a = true;
if (!key_state.w && !key_state.s) {
x_rotate = 0; // Stop auto-rolling if applicable
}
break;
case "KeyD":
case "ArrowRight":
z_rotate = -speed; // Move right
key_state.d = true;
if (!key_state.w && !key_state.s) {
x_rotate = 0; // Stop auto-rolling if applicable
}
break;
}
}
function OnKeyUp(event) {
switch (event.code) {
case "KeyW":
case "ArrowUp":
key_state.w = false;
if (z_rotate !== 0 && x_rotate === -speed) {
x_rotate = 0; // Only stop if moving diagonally, otherwise continue
}
if (key_state.s) {
x_rotate = speed; // Adjust to the opposite direction if that key is pressed
}
break;
case "KeyS":
case "ArrowDown":
key_state.s = false;
if (z_rotate !== 0 && x_rotate === speed) {
x_rotate = 0;
}
if (key_state.w) {
x_rotate = -speed;
}
break;
case "KeyA":
case "ArrowLeft":
key_state.a = false;
if (x_rotate !== 0 && z_rotate === speed) {
z_rotate = 0;
}
if (key_state.d) {
z_rotate = -speed;
}
break;
case "KeyD":
case "ArrowRight":
key_state.d = false;
if (x_rotate !== 0 && z_rotate === -speed) {
z_rotate = 0;
}
if (key_state.a) {
z_rotate = speed;
}
break;
}
}
This method keeps the ball from stopping entirely while allowing up to three simultaneous key inputs.
Writing a Script to Jump Using the Spacebar
a. Retrieve the camera object.
b. Add space to key_state and handle the Spacebar input.
c. Create a function to move the player object and camera to simulate jumping.
...
const camera = WORLD.getObject("MainCamera");
let jump_up; // Timeout for jump-up movement
let jump_down; // Timeout for jump-down movement
let is_jump = false; // Check if jumping
function Update(dt) {
...
if (key_state.space && !is_jump) {
do_jump(); // Only jump if Spacebar is pressed and not already jumping
}
}
function OnKeyDown(event) {
switch (event.code) {
...
case "Space":
key_state.space = true;
break;
}
}
function OnKeyUp(event) {
switch (event.code) {
...
case "Space":
key_state.space = false;
break;
}
}
function do_jump() {
is_jump = true; // Set to true at the start of the jump
body.move(0, 3, 0, 0.5);
camera.move(0, 3, 0, 0.5); // Adjust y-value of both player body and camera
jump_up = setTimeout(() => {
body.move(0, -3, 0, 0.5);
camera.move(0, -3, 0, 0.5);
jump_down = setTimeout(() => {
is_jump = false; // Set back to false after jump ends
}, 550)
}, 550)
}
Storing Timeouts or Intervals as objects can help with clearing or preventing duplication.
Creating a Trail Object to Follow the Player
Copy the player object, remove the camera, and rename the objects to trail and trail_body, respectively.
Creating a Trail Class and Writing a Script to Continuously Generate Trails at the Player’s Position
a. Retrieve the trail and trail_body objects.
b. Create a Trail class and set the object and position information in the constructor.
c. Specify the number of clones, create them, and store them as instances of the Trail class.
d. Generate trail clones at the player’s position at regular intervals.
const trail = WORLD.getObject("trail");
const trail_body = WORLD.getObject("trail_body");
const max_trail = 40; // Set maximum number of clones
const trails = []; // Store clones
let now_trail = 0; // Current clone number
let trail_interval;
function Start() {
for (let i = 0; i < max_trail; i++) { // Pre-generate clones as needed
const trail_clone = trail.cloneWithMethods();
WORLD.add(trail_clone);
trails.push(new Trail(trail_clone)); // Classify clones
}
make_trail();
}
// Function to leave trails at the player's position every 0.1 seconds
function make_trail() {
trail_interval = setInterval(() => {
trails[now_trail].spawn_trail();
now_trail++;
if (now_trail === max_trail) {
now_trail = 0;
}
}, 100)
}
class Trail {
constructor(object) {
this.object = object;
this.trail_position = new THREE.Vector3();
this.timeout;
}
spawn_trail() {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.reset_trail();
// Match the trail and body positions to the player's
this.object.position.copy(player.position);
this.object.rotation.copy(player.rotation);
this.object.children[0].position.copy(player_body.position);
this.object.children[0].rotation.copy(player_body.rotation);
this.object.children[0].getWorldPosition(this.trail_position);
// Disappear after 4 seconds
this.timeout = setTimeout(() => {
this.reset_trail();
}, 4000)
}
// Move the object far away to simulate removal from the World
reset_trail() {
this.object.position.set(0, 300, 0);
}
}
getWorldPosition
allows for future distance calculations with trail_body
.
player_body
can also be used with getWorldPosition
for comparison.