Skip to content
This repository was archived by the owner on Dec 1, 2023. It is now read-only.

Babylon character controller implemented #1

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
jumps height fixed, character ray fixed, jump delay added, borders an…
…d grass added
Андрей Кухоренко committed Jan 26, 2022
commit 2dccad80637929a617a619f94da5e89d54519563
5 changes: 1 addition & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -10,10 +10,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
project: [
resolve(__dirname, './tsconfig.json'),
resolve(__dirname, './tsconfig.eslint.json'),
],
project: './tsconfig.json',
ecmaVersion: 13,
parser: '@typescript-eslint/parser',
sourceType: 'module',
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -25,7 +25,6 @@
},
"homepage": "https://github.com/LeadrateMSK/webpack-starter#readme",
"devDependencies": {
"@babylonjs/loaders": "^4.2.1",
"@types/cannon": "^0.1.8",
"@types/earcut": "^2.1.1",
"@typescript-eslint/eslint-plugin": "^5.9.0",
@@ -52,6 +51,7 @@
"vue-template-compiler": "^2.6.14"
},
"dependencies": {
"@babylonjs/loaders": "^4.2.1",
"@babylonjs/core": "^4.2.1",
"@babylonjs/inspector": "^4.2.1",
"babylonjs-gui": "^4.2.1",
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added src/assets/textures/grass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
1 change: 0 additions & 1 deletion src/img/textures/dude.babylon

This file was deleted.

3 changes: 2 additions & 1 deletion src/ts/Camera.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {
ArcRotateCamera,
FollowCamera,
Scene,
Vector3,
} from '@babylonjs/core';

export class Camera {
scene: Scene;

canvas: HTMLCanvasElement;

camera: FollowCamera;

constructor(scene: Scene, canvas: HTMLCanvasElement) {
39 changes: 33 additions & 6 deletions src/ts/GUI.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
import { Scene } from '@babylonjs/core';
import { Engine, Scene } from '@babylonjs/core';
import * as GUI from 'babylonjs-gui';

export class CustomGUI {
scene: Scene;

constructor(scene: Scene) {
engine: Engine;

advancedTexture: GUI.AdvancedDynamicTexture;

constructor(scene: Scene, engine: Engine) {
this.scene = scene;
this.engine = engine;
this.create();
this.showFps();
}

create() {
private create() {
const advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI', true, this.scene);

const instructions = new GUI.TextBlock();
instructions.text = 'Move w/ WASD keys, Space for Jump, look with the mouse';
instructions.color = 'white';
instructions.fontSize = 20;
instructions.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
instructions.textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
instructions.paddingBottomInPixels = 30;
instructions.paddingRight = 30;
instructions.paddingRightInPixels = 30;
instructions.text = 'Move w/ WASD keys, Space for Jump, look with the mouse';

advancedTexture.addControl(instructions);

this.advancedTexture = advancedTexture;
}

private showFps() {
const fps = new GUI.TextBlock();
fps.color = 'white';
fps.fontSize = 25;
fps.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
fps.textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
fps.paddingTopInPixels = 30;
fps.paddingRightInPixels = 30;
fps.outlineColor = 'black';
fps.outlineWidth = 4;

this.advancedTexture.addControl(fps);

this.scene.onBeforeRenderObservable.add(() => {
const frameRate = this.engine.getFps().toFixed();
fps.text = `${frameRate} fps`;
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
/* eslint-disable max-len */
import {
AbstractMesh,
AnimationGroup,
Engine,
FollowCamera,
KeyboardEventTypes,
Ray,
RayHelper,
Scene,
SceneLoader,
ShadowGenerator,
Vector3,
} from '@babylonjs/core';
import "@babylonjs/loaders/glTF";
import '@babylonjs/loaders/glTF';

export class PlayerController {
export class CharacterController {
scene: Scene;

camera: FollowCamera;

engine: Engine;

shadowGenerator: ShadowGenerator;
groundSize: { width: number, height: number }


groundSize: { width: number, height: number };

constructor(scene: Scene, camera: FollowCamera, engine: Engine, shadowGenerator: ShadowGenerator, groundSize: { width: number, height: number }) {
this.scene = scene;
this.camera = camera;
@@ -28,32 +34,52 @@ export class PlayerController {
this.create();
}

private readonly jumpHeight: number = 1.5;
private maxJumpHeight = 1.5;

private jumpHeight = 0;

private character: AbstractMesh;

private isGrounded: boolean;
private isJumped: boolean = false;
private isWalkingBackAnimated: boolean = false;
private isWalkingAnimated: boolean = false;
private isRunningAnimated: boolean = false;

private isJumped = false;

private jumpDelayFrames = 0;

private isWalkingBackAnimated = false;

private isWalkingAnimated = false;

private isRunningAnimated = false;

private inputs: { [index: string]: boolean } = {};

private idleAnim: AnimationGroup;

private runAnim: AnimationGroup;

private walkAnim: AnimationGroup;

private jumpAnim: AnimationGroup;

private walkBackAnim: AnimationGroup;

public create() {
SceneLoader.ImportMeshAsync('', '../../img/models/', 'bot.glb', this.scene).then(result => {
SceneLoader.ImportMeshAsync('', '../../assets/models/', 'bot.glb', this.scene).then((result) => {
const character = result.meshes[0];
character.checkCollisions = true;
character.ellipsoid = new Vector3(0.5, 1, 0.5);
character.ellipsoidOffset = new Vector3(0, 1, 0);
// character.physicsImpostor = new PhysicsImpostor(character, PhysicsImpostor.BoxImpostor, { mass: 1, restitution: 0 });

this.idleAnim = this.scene.getAnimationGroupByName('Idle');
this.runAnim = this.scene.getAnimationGroupByName('Run');
this.walkAnim = this.scene.getAnimationGroupByName('Walk');
this.jumpAnim = this.scene.getAnimationGroupByName('Jump');
this.walkBackAnim = this.scene.getAnimationGroupByName('Walk_Back');

this.shadowGenerator.addShadowCaster(character);

this.camera.lockedTarget = character;
this.character = character;

@@ -69,7 +95,6 @@ export class PlayerController {
this.setIsGrounded();
this.processJump();
this.processMoving();

}

private addInputListeners() {
@@ -88,94 +113,89 @@ export class PlayerController {
}

private setIsGrounded() {
const ray = new Ray(this.character.position, new Vector3(0, 0.05, 0), 0.05);
const ray = new Ray(this.character.position, new Vector3(0, -0.15, 0), 0.15);

const pick = this.scene.pickWithRay(ray);
// const rayHelper = new RayHelper(ray);
// rayHelper.show(this.scene);

if (pick) this.isGrounded = pick.hit;
}

private processMoving() {
const { character, inputs } = this;
const deltaTime = this.engine.getDeltaTime();
const speed = 0.003 * deltaTime;
const speed = 0.002 * deltaTime;
const backwardSpeed = 0.0015 * deltaTime;
const runningSpeed = 0.006 * deltaTime;
const rotationSpeed = 0.0015 * deltaTime;
const isRunning: boolean = inputs.ShiftLeft;
let isKeyDown = false;
let isRunning: boolean = inputs['ShiftLeft'];

// Return character in ground center if he tries to leave the ground
if (character.position.z > this.groundSize.height / 2 || character.position.z < -(this.groundSize.height / 2)) character.position = Vector3.Zero();
if (character.position.x > this.groundSize.width / 2 || character.position.x < -(this.groundSize.width / 2)) character.position = Vector3.Zero();

if (inputs['KeyW']) {
if (inputs.KeyW) {
isKeyDown = true;
if (isRunning) {
character.moveWithCollisions(character.forward.scaleInPlace(runningSpeed));
} else {
character.moveWithCollisions(character.forward.scaleInPlace(speed));
}
} else if (inputs['KeyS'] && !this.isWalkingAnimated) {
} else if (inputs.KeyS && !this.isWalkingAnimated) {
isKeyDown = true;
character.moveWithCollisions(character.forward.scaleInPlace(-backwardSpeed));
}

if (inputs['Space']) {
if (inputs.Space && this.jumpDelayFrames <= 0) {
isKeyDown = true;
}

if (inputs['KeyA']) {
if (inputs.KeyA) {
isKeyDown = true;
character.rotate(Vector3.Up(), -rotationSpeed);
} else if (this.inputs['KeyD']) {
} else if (this.inputs.KeyD) {
isKeyDown = true;
character.rotate(Vector3.Up(), rotationSpeed);
}

if (this.isGrounded) {
if (isKeyDown) {
if (inputs['Space']) {
if (inputs.Space && this.jumpDelayFrames <= 0) {
this.jump();
this.isJumped = true;
this.isRunningAnimated = false;
this.isWalkingAnimated = false;

} else if (inputs['KeyW'] && isRunning) {
if(!this.isRunningAnimated) {
} else if (inputs.KeyW && isRunning) {
if (!this.isRunningAnimated) {
this.run();
this.isRunningAnimated = true;
this.isWalkingAnimated = false;
}

} else if (inputs['KeyW'] && this.isWalkingBackAnimated) {
} else if (inputs.KeyW && this.isWalkingBackAnimated) {
// on W key press with pressed key S (W key has priority)
this.walkForward();
this.isWalkingBackAnimated = false;
this.isRunningAnimated = false;
this.isWalkingAnimated = true;

} else if (inputs['KeyS'] && !this.isWalkingBackAnimated && !this.isWalkingAnimated) {
} else if (inputs.KeyS && !this.isWalkingBackAnimated && !this.isWalkingAnimated) {
this.walkBackward();
this.isRunningAnimated = false;
this.isWalkingBackAnimated = true;

} else if (!this.isWalkingAnimated && !this.isWalkingBackAnimated) {
// case to animate rotations
this.walkForward();
this.isRunningAnimated = false;
this.isWalkingAnimated = true;

}
} else {
if (this.isWalkingAnimated || this.isRunningAnimated || this.isWalkingBackAnimated) {
this.idle();
this.isWalkingAnimated = false;
this.isRunningAnimated = false;
this.isWalkingBackAnimated = false;

}
} else if (this.isWalkingAnimated || this.isRunningAnimated || this.isWalkingBackAnimated) {
// if no key pressed stop all animations
this.idle();
this.isWalkingAnimated = false;
this.isRunningAnimated = false;
this.isWalkingBackAnimated = false;
}

}
}

@@ -185,15 +205,20 @@ export class PlayerController {
const fallingForce = 0.008 * deltaTime;

if (this.isJumped) {
if (this.character.position.y < this.jumpHeight) {
if (this.jumpHeight < this.maxJumpHeight) {
this.jumpHeight += jumpForce;
this.character.moveWithCollisions(this.character.up.scaleInPlace(jumpForce));
} else {
this.jumpHeight = 0;
this.isJumped = false;
this.jumpDelayFrames = 130; // add counter to delay next jump
}
} else if (!this.isGrounded) {
this.character.moveWithCollisions(this.character.up.scaleInPlace(-fallingForce));
if(this.character.position.y < 0) this.character.position.y = 0;
if (this.character.position.y < 0) this.character.position.y = 0;
}

if (this.jumpDelayFrames > 0) this.jumpDelayFrames -= 0.2 * deltaTime;
}

public run() {
@@ -214,7 +239,7 @@ export class PlayerController {
public jump() {
this.stopAnims();
// Variable to hide animtaion jump delay
const jumpAnimFrom = 0.75 ;
const jumpAnimFrom = 0.75;
this.jumpAnim.start(false, 1.0, jumpAnimFrom, this.jumpAnim.to, false);
}

@@ -224,11 +249,14 @@ export class PlayerController {
}

private stopAnims() {
this.idleAnim.stop();
this.runAnim.stop();
this.walkAnim.stop();
this.jumpAnim.stop();
this.walkBackAnim.stop();
}
this.idleAnim.stop();
this.runAnim.stop();
this.walkAnim.stop();
this.jumpAnim.stop();
this.walkBackAnim.stop();
}

public getCharacter() {
return this.character;
}
}
Loading