https://playcanvas.com/project/721032/overview/test
I have gotten the WASD movement in but I don´t know how to code so I’m not able to add a jump feature how can I do this.
https://playcanvas.com/project/721032/overview/test
I have gotten the WASD movement in but I don´t know how to code so I’m not able to add a jump feature how can I do this.
Hi @wolfshadow726 and welcome,
Try doing a search in the forums with “how to jump”, you will get several relevant posts to read through.
I did but I did not find anything that had both was and jump for first-person and anything I did find broke it so I more help.
It’s not easy to make a jump feature and it depends on your game your wishes and your abilities.
The way I do that in my game is that I move the character up for a short while and then I move the character down, until the character reaches the ground. But I also check how far the player wants to jump and apply that in the jump by moving the character forward at the same time.
But how you do this is completely dependent on the rest of the game.
ok, I would do that if I knew how but I have no idea how to code at all.
A good start is to follow the PlayCanvas tutorial. Than you learn the basics that you need to make a game.
https://developer.playcanvas.com/en/tutorials/keepyup-part-one/
ok thank you very much I will try it
hey so do you think you could try and add a jump feature to my code because I want a momentum-based game.
var FirstPersonMovement = pc.createScript('firstPersonMovement');
FirstPersonMovement.attributes.add('camera', {
type: 'entity',
description: 'Optional, assign a camera entity, otherwise one is created'
});
FirstPersonMovement.attributes.add('power', {
type: 'number',
default: 2500,
description: 'Adjusts the speed of player movement'
});
FirstPersonMovement.attributes.add('lookSpeed', {
type: 'number',
default: 0.25,
description: 'Adjusts the sensitivity of looking'
});
// initialize code called once per entity
FirstPersonMovement.prototype.initialize = function() {
this.force = new pc.Vec3();
this.eulers = new pc.Vec3();
var app = this.app;
// Listen for mouse move events
app.mouse.on("mousemove", this._onMouseMove, this);
// when the mouse is clicked hide the cursor
app.mouse.on("mousedown", function () {
app.mouse.enablePointerLock();
}, this);
// Check for required components
if (!this.entity.collision) {
console.error("First Person Movement script needs to have a 'collision' component");
}
if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
console.error("First Person Movement script needs to have a DYNAMIC 'rigidbody' component");
}
};
// update code called every frame
FirstPersonMovement.prototype.update = function(dt) {
// If a camera isn't assigned from the Editor, create one
if (!this.camera) {
this._createCamera();
}
var force = this.force;
var app = this.app;
// Get camera directions to determine movement directions
var forward = this.camera.forward;
var right = this.camera.right;
// movement
var x = 0;
var z = 0;
// Use W-A-S-D keys to move player
// Check for key presses
if (app.keyboard.isPressed(pc.KEY_A) || app.keyboard.isPressed(pc.KEY_Q)) {
x -= right.x;
z -= right.z;
}
if (app.keyboard.isPressed(pc.KEY_D)) {
x += right.x;
z += right.z;
}
if (app.keyboard.isPressed(pc.KEY_W)) {
x += forward.x;
z += forward.z;
}
if (app.keyboard.isPressed(pc.KEY_S)) {
x -= forward.x;
z -= forward.z;
}
// use direction from keypresses to apply a force to the character
if (x !== 0 && z !== 0) {
force.set(x, 0, z).normalize().scale(this.power);
this.entity.rigidbody.applyForce(force);
}
// update camera angle from mouse events
this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
};
FirstPersonMovement.prototype._onMouseMove = function (e) {
// If pointer is disabled
// If the left mouse button is down update the camera from mouse movement
if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
this.eulers.x -= this.lookSpeed * e.dx;
this.eulers.y -= this.lookSpeed * e.dy;
}
};
FirstPersonMovement.prototype._createCamera = function () {
// If user hasn't assigned a camera, create a new one
this.camera = new pc.Entity();
this.camera.setName("First Person Camera");
this.camera.addComponent("camera");
this.entity.addChild(this.camera);
this.camera.translateLocal(0, 0.5, 0);
};
this.x = new pc.Vec3();
this.z = new pc.Vec3();
this.heading = new pc.Vec3();
this.magnitude = new pc.Vec2();
this.azimuth = 0;
this.elevation = 0;
// Calculate camera azimuth/elevation
var temp = this.camera.forward.clone();
temp.y = 0;
temp.normalize();
this.azimuth = Math.atan2(-temp.x, -temp.z) * (180 / Math.PI);
var rot = new pc.Mat4().setFromAxisAngle(pc.Vec3.UP, -this.azimuth);
rot.transformVector(this.camera.forward, temp);
this.elevation = Math.atan(temp.y, temp.z) * (180 / Math.PI);
this.forward = 0;
this.strafe = 0;
this.jump = false;
this.cnt = 0;
app.on('firstperson:forward', function (value) {
this.forward = value;
}, this);
app.on('firstperson:strafe', function (value) {
this.strafe = value;
}, this);
app.on('firstperson:look', function (azimuthDelta, elevationDelta) {
this.azimuth += azimuthDelta;
this.elevation += elevationDelta;
this.elevation = pc.math.clamp(this.elevation, -90, 90);
}, this);
app.on('firstperson:jump', function () {
this.jump = true;
}, this);
};
FirstPersonView.prototype.postUpdate = function(dt) {
// Update the camera's orientation
this.camera.setEulerAngles(this.elevation, this.azimuth, 0);
// Calculate the camera's heading in the XZ plane
this.z.copy(this.camera.forward);
this.z.y = 0;
this.z.normalize();
this.x.copy(this.camera.right);
this.x.y = 0;
this.x.normalize();
this.heading.set(0, 0, 0);
// Move forwards/backwards
if (this.forward !== 0) {
this.z.scale(this.forward);
this.heading.add(this.z);
}
// Strafe left/right
if (this.strafe !== 0) {
this.x.scale(this.strafe);
this.heading.add(this.x);
}
if (this.heading.length() > 0.0001) {
this.magnitude.set(this.forward, this.strafe);
this.heading.normalize().scale(this.magnitude.length());
}
if (this.jump) {
this.entity.script.characterController.jump();
this.jump = false;
}
this.entity.script.characterController.move(this.heading);
var pos = this.camera.getPosition();
this.app.fire('cameramove', pos);
};
////////////////////////////////////////////////////////////////////////////////
// FPS Keyboard Controls (Movement Only - Work Alongside Mouse Look Script) //
////////////////////////////////////////////////////////////////////////////////
var KeyboardInput = pc.createScript('keyboardInput');
KeyboardInput.prototype.initialize = function() {
var app = this.app;
var updateMovement = function (keyCode, value) {
switch (keyCode) {
case 38: // Up arrow
case 87: // W
app.fire('firstperson:forward', value);
break;
case 40: // Down arrow
case 83: // S
app.fire('firstperson:forward', -value);
break;
case 37: // Left arrow
case 65: // A
app.fire('firstperson:strafe', -value);
break;
case 39: // Right arrow
case 68: // D
app.fire('firstperson:strafe', value);
break;
}
};
var keyDown = function (e) {
if (!e.repeat) {
updateMovement(e.keyCode, 1);
if (e.keyCode === 32) { // Space
app.fire('firstperson:jump');
}
}
};
var keyUp = function (e) {
updateMovement(e.keyCode, 0);
};
// Manage DOM event listeners
var addEventListeners = function () {
window.addEventListener('keydown', keyDown, true);
window.addEventListener('keyup', keyUp, true);
};
var removeEventListeners = function () {
window.addEventListener('keydown', keyDown, true);
window.addEventListener('keyup', keyUp, true);
};
this.on('enable', addEventListeners);
this.on('disable', removeEventListeners);
addEventListeners();
};
////////////////////////////////////////////////////////////////////////////////
// FPS Mouse Look Controls //
////////////////////////////////////////////////////////////////////////////////
var MouseInput = pc.createScript('mouseInput');
MouseInput.prototype.initialize = function() {
var app = this.app;
var canvas = app.graphicsDevice.canvas;
var mouseDown = function (e) {
if (document.pointerLockElement !== canvas && canvas.requestPointerLock) {
canvas.requestPointerLock();
}
};
var mouseMove = function (e) {
if (document.pointerLockElement === canvas) {
var movementX = event.movementX || event.webkitMovementX || event.mozMovementX || 0;
var movementY = event.movementY || event.webkitMovementY || event.mozMovementY || 0;
app.fire('firstperson:look', -movementX / 5, -movementY / 5);
}
};
// Manage DOM event listeners
var addEventListeners = function () {
window.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mousemove', mouseMove, false);
};
var removeEventListeners = function () {
window.removeEventListener('mousedown', mouseDown, false);
window.removeEventListener('mousemove', mouseMove, false);
};
this.on('enable', addEventListeners);
this.on('disable', removeEventListeners);
addEventListeners();
};
// Utility function for both touch and gamepad handling of deadzones
// Takes a 2-axis joystick position in the range -1 to 1 and applies
// an upper and lower radial deadzone, remapping values in the legal
// range from 0 to 1.
function applyRadialDeadZone(pos, remappedPos, deadZoneLow, deadZoneHigh) {
var magnitude = pos.length();
if (magnitude > deadZoneLow) {
var legalRange = 1 - deadZoneHigh - deadZoneLow;
var normalizedMag = Math.min(1, (magnitude - deadZoneLow) / legalRange);
var scale = normalizedMag / magnitude;
remappedPos.copy(pos).scale(scale);
} else {
remappedPos.set(0, 0);
}
}
////////////////////////////////////////////////////////////////////////////////
// Dual Virtual Stick FPS Touch Controls //
////////////////////////////////////////////////////////////////////////////////
var TouchInput = pc.createScript('touchInput');
TouchInput.attributes.add('deadZone', {
title: 'Dead Zone',
description: 'Radial thickness of inner dead zone of the virtual joysticks. This dead zone ensures the virtual joysticks report a value of 0 even if a touch deviates a small amount from the initial touch.',
type: 'number',
min: 0,
max: 0.4,
default: 0.3
});
TouchInput.attributes.add('turnSpeed', {
title: 'Turn Speed',
description: 'Maximum turn speed in degrees per second',
type: 'number',
default: 150
});
TouchInput.attributes.add('radius', {
title: 'Radius',
description: 'The radius of the virtual joystick in CSS pixels.',
type: 'number',
default: 50
});
TouchInput.attributes.add('doubleTapInterval', {
title: 'Double Tap Interval',
description: 'The time in milliseconds between two taps of the right virtual joystick for a double tap to register. A double tap will trigger a jump.',
type: 'number',
default: 300
});
TouchInput.prototype.initialize = function() {
var app = this.app;
var graphicsDevice = app.graphicsDevice;
var canvas = graphicsDevice.canvas;
this.remappedPos = new pc.Vec2();
this.leftStick = {
identifier: -1,
center: new pc.Vec2(),
pos: new pc.Vec2()
};
this.rightStick = {
identifier: -1,
center: new pc.Vec2(),
pos: new pc.Vec2()
};
this.lastRightTap = 0;
var touchStart = function (e) {
e.preventDefault();
var xFactor = graphicsDevice.width / canvas.clientWidth;
var yFactor = graphicsDevice.height / canvas.clientHeight;
var touches = e.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
if (touch.pageX <= canvas.clientWidth / 2 && this.leftStick.identifier === -1) {
// If the user touches the left half of the screen, create a left virtual joystick...
this.leftStick.identifier = touch.identifier;
this.leftStick.center.set(touch.pageX, touch.pageY);
this.leftStick.pos.set(0, 0);
app.fire('leftjoystick:enable', touch.pageX * xFactor, touch.pageY * yFactor);
} else if (touch.pageX > canvas.clientWidth / 2 && this.rightStick.identifier === -1) {
// ...otherwise create a right virtual joystick
this.rightStick.identifier = touch.identifier;
this.rightStick.center.set(touch.pageX, touch.pageY);
this.rightStick.pos.set(0, 0);
app.fire('rightjoystick:enable', touch.pageX * xFactor, touch.pageY * yFactor);
// See how long since the last tap of the right virtual joystick to detect a double tap (jump)
var now = Date.now();
if (now - this.lastRightTap < this.doubleTapInterval) {
app.fire('firstperson:jump');
}
this.lastRightTap = now;
}
}
}.bind(this);
var touchMove = function (e) {
e.preventDefault();
var xFactor = graphicsDevice.width / canvas.clientWidth;
var yFactor = graphicsDevice.height / canvas.clientHeight;
var touches = e.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// Update the current positions of the two virtual joysticks
if (touch.identifier === this.leftStick.identifier) {
this.leftStick.pos.set(touch.pageX, touch.pageY);
this.leftStick.pos.sub(this.leftStick.center);
this.leftStick.pos.scale(1 / this.radius);
app.fire('leftjoystick:move', touch.pageX * xFactor, touch.pageY * yFactor);
} else if (touch.identifier === this.rightStick.identifier) {
this.rightStick.pos.set(touch.pageX, touch.pageY);
this.rightStick.pos.sub(this.rightStick.center);
this.rightStick.pos.scale(1 / this.radius);
app.fire('rightjoystick:move', touch.pageX * xFactor, touch.pageY * yFactor);
}
}
}.bind(this);
var touchEnd = function (e) {
e.preventDefault();
var touches = e.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
// If this touch is one of the sticks, get rid of it...
if (touch.identifier === this.leftStick.identifier) {
this.leftStick.identifier = -1;
app.fire('firstperson:forward', 0);
app.fire('firstperson:strafe', 0);
app.fire('leftjoystick:disable');
} else if (touch.identifier === this.rightStick.identifier) {
this.rightStick.identifier = -1;
app.fire('rightjoystick:disable');
}
}
}.bind(this);
// Manage DOM event listeners
var addEventListeners = function () {
canvas.addEventListener('touchstart', touchStart, false);
canvas.addEventListener('touchmove', touchMove, false);
canvas.addEventListener('touchend', touchEnd, false);
};
var removeEventListeners = function () {
canvas.removeEventListener('touchstart', touchStart, false);
canvas.removeEventListener('touchmove', touchMove, false);
canvas.removeEventListener('touchend', touchEnd, false);
};
this.on('enable', addEventListeners);
this.on('disable', removeEventListeners);
addEventListeners();
};
TouchInput.prototype.update = function(dt) {
var app = this.app;
// Moving
if (this.leftStick.identifier !== -1) {
// Apply a lower radial dead zone. We don't need an upper zone like with a real joypad
applyRadialDeadZone(this.leftStick.pos, this.remappedPos, this.deadZone, 0);
var strafe = this.remappedPos.x;
if (this.lastStrafe !== strafe) {
app.fire('firstperson:strafe', strafe);
this.lastStrafe = strafe;
}
var forward = -this.remappedPos.y;
if (this.lastForward !== forward) {
app.fire('firstperson:forward', forward);
this.lastForward = forward;
}
}
// Looking
if (this.rightStick.identifier !== -1) {
// Apply a lower radial dead zone. We don't need an upper zone like with a real joypad
applyRadialDeadZone(this.rightStick.pos, this.remappedPos, this.deadZone, 0);
var lookLeftRight = -this.remappedPos.x * this.turnSpeed * dt;
var lookUpDown = -this.remappedPos.y * this.turnSpeed * dt;
app.fire('firstperson:look', lookLeftRight, lookUpDown);
}
};
////////////////////////////////////////////////////////////////////////////////
// Dual Analog Stick FPS Gamepad Controls //
////////////////////////////////////////////////////////////////////////////////
var GamePadInput = pc.createScript('gamePadInput');
GamePadInput.attributes.add('deadZoneLow', {
title: 'Low Dead Zone',
description: 'Radial thickness of inner dead zone of pad\'s joysticks. This dead zone ensures that all pads report a value of 0 for each joystick axis when untouched.',
type: 'number',
min: 0,
max: 0.4,
default: 0.1
});
GamePadInput.attributes.add('deadZoneHigh', {
title: 'High Dead Zone',
description: 'Radial thickness of outer dead zone of pad\'s joysticks. This dead zone ensures that all pads can reach the -1 and 1 limits of each joystick axis.',
type: 'number',
min: 0,
max: 0.4,
default: 0.1
});
GamePadInput.attributes.add('turnSpeed', {
title: 'Turn Speed',
description: 'Maximum turn speed in degrees per second',
type: 'number',
default: 90
});
GamePadInput.prototype.initialize = function() {
var app = this.app;
this.lastStrafe = 0;
this.lastForward = 0;
this.lastJump = false;
this.remappedPos = new pc.Vec2();
this.leftStick = {
center: new pc.Vec2(),
pos: new pc.Vec2()
};
this.rightStick = {
center: new pc.Vec2(),
pos: new pc.Vec2()
};
// Manage DOM event listeners
var addEventListeners = function () {
window.addEventListener("gamepadconnected", function(e) {});
window.addEventListener("gamepaddisconnected", function(e) {});
};
var removeEventListeners = function () {
window.removeEventListener("gamepadconnected", function(e) {});
window.removeEventListener("gamepaddisconnected", function(e) {});
};
this.on('enable', addEventListeners);
this.on('disable', removeEventListeners);
addEventListeners();
};
GamePadInput.prototype.update = function(dt) {
var app = this.app;
var gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
for (var i = 0; i < gamepads.length; i++) {
var gamepad = gamepads[i];
// Only proceed if we have at least 2 sticks
if (gamepad && gamepad.mapping === 'standard' && gamepad.axes.length >= 4) {
// Moving (left stick)
this.leftStick.pos.set(gamepad.axes[0], gamepad.axes[1]);
applyRadialDeadZone(this.leftStick.pos, this.remappedPos, this.deadZoneLow, this.deadZoneHigh);
var strafe = this.remappedPos.x;
if (this.lastStrafe !== strafe) {
app.fire('firstperson:strafe', strafe);
this.lastStrafe = strafe;
}
var forward = -this.remappedPos.y;
if (this.lastForward !== forward) {
app.fire('firstperson:forward', forward);
this.lastForward = forward;
}
// Looking (right stick)
this.rightStick.pos.set(gamepad.axes[2], gamepad.axes[3]);
applyRadialDeadZone(this.rightStick.pos, this.remappedPos, this.deadZoneLow, this.deadZoneHigh);
var lookLeftRight = -this.remappedPos.x * this.turnSpeed * dt;
var lookUpDown = -this.remappedPos.y * this.turnSpeed * dt;
app.fire('firstperson:look', lookLeftRight, lookUpDown);
// Jumping (bottom button of right cluster)
if (gamepad.buttons[0].pressed && !this.lastJump) {
app.fire('firstperson:jump');
}
this.lastJump = gamepad.buttons[0].pressed;
}
}
};
ps i have no idea how this works
Hello @wolfshadow726! Did you already follow the PlayCanvas tutorial? This tutorial has also a kind of jumping by keeping the ball up and you will learn the first things that you need to create your game. If you already have a project with the character movement and envorniment, I would like to see it so that I can help you better.
Ok, I have the link here. https://playcanvas.com/project/721032/overview/reclaw
i normally jump by checking if the player is at a certain height, say,
if(this.entity.getPosition.y < //put your max height here){
this.entity.rigidbody.applyImpulse(0,10,0);
}
does that help???
I’m not sure because I don’t know where to add it in my code
Also sorry for taking so long my computer had broke and I’m still fixing it but I can still add code
With this solution you have to be careful if you have for example a level with different floors.
put the code in the update function.
I have changed it a little bit
if(this.entity.getPosition.y < 2){
this.entity.rigidbody.applyImpulse(0,40,0);
}
you can put it anywhere in the update function
i hope that helped
Little correction in your if statement:
if (this.entity.getPosition().y < 2) {
this.entity.rigidbody.applyImpulse(0,40,0);
}