Skip to content

Commit

Permalink
Merge pull request #1 from default-username-852/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
default-username-852 authored Aug 2, 2024
2 parents 77ef0ed + 9b854b3 commit 652bbdb
Show file tree
Hide file tree
Showing 19 changed files with 450 additions and 107 deletions.
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# TODO

- hinder
- bättre modeller + texturer
- mer varierade banor
- roliga powerups?
Binary file removed assets/gravel.png
Binary file not shown.
Binary file added assets/soundtrack.mp3
Binary file not shown.
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ declare module "*.glb" {
const value: any;
export default value;
}

declare module "*.mp3" {
const value: any;
export default value;
}
6 changes: 4 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
<html>
<head>
<meta charset="utf-8" />
<meta content='width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;' name='viewport' />
<meta name="viewport" content="width=device-width" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body style="margin: 0px;">
<div id="root" style="width: 100%; height: 100vh;"></div>
<body style="margin: 0px; touch-action: none;">
<div id="root" style="width: 100%; height: 100dvh;"></div>
</body>
</html>
70 changes: 66 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { createRoot } from 'react-dom/client';
import * as React from 'react';
import GameView from './view/GameView';
import { Game, GameInstance } from './model/Game';
import { Game } from './model/Game';
import { proxy } from 'valtio';
import * as Stats from 'stats.js';

// TODO: add support for swipe gestures

let stats = new Stats();
document.body.appendChild(stats.dom);
requestAnimationFrame(function loop() {
Expand All @@ -17,9 +15,73 @@ requestAnimationFrame(function loop() {
export const gameState = proxy(new Game());

document.addEventListener('keydown', (event) => {
gameState.keyboardEvent(event as KeyboardEvent);
switch (event.code) {
case "ArrowUp":
gameState.gesture("up");
break;
case "ArrowDown":
gameState.gesture("down");
break;
case "ArrowLeft":
gameState.gesture("left");
break;
case "ArrowRight":
gameState.gesture("right");
break;
default:
break;
}
});

document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchmove', handleTouchMove, false);

var xDown: number | null = null;
var yDown: number | null = null;

function getTouches(evt: TouchEvent): TouchList {
return evt.touches;
}

function handleTouchStart(evt: TouchEvent) {
const firstTouch = getTouches(evt)[0];
xDown = firstTouch.clientX;
yDown = firstTouch.clientY;
};

function handleTouchMove(evt: TouchEvent) {
if ( ! xDown || ! yDown ) {
return;
}

var xUp = evt.touches[0].clientX;
var yUp = evt.touches[0].clientY;

var xDiff = xDown - xUp;
var yDiff = yDown - yUp;

if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
if ( xDiff < 0 ) {
/* right swipe */
gameState.gesture("right");
} else {
/* left swipe */
gameState.gesture("left");
}
} else {
if ( yDiff < 0 ) {
/* down swipe */
gameState.gesture("down");
} else {
/* up swipe */
gameState.gesture("up");
}
}
/* reset values */
xDown = null;
yDown = null;
};

let lastTime = 0;
// delta is in milliseconds
function gameLoop(time: DOMHighResTimeStamp) {
Expand Down
70 changes: 46 additions & 24 deletions src/model/Game.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Terrain, TerrainType } from "./Terrain";
import Player from "./Player";
import { Terrain } from "./Terrain";
import Player, { RunningState } from "./Player";
import { Lane } from "./Lane";
import { SEGMENTS } from "./Segment";
import { TerrainManager } from "./TerrainManager";
Expand All @@ -8,6 +8,7 @@ import { LOW_OBSTACLE_HEIGHT, ObstacleType } from "./Obstacle";
const INCREASE_SPEED_RATE: number = 0.01;

export class Game {
started: boolean = false;
currentInstance: GameInstance = new GameInstance();
bestScore: number | undefined = undefined;

Expand All @@ -17,17 +18,14 @@ export class Game {
}

update(delta: number) {
this.currentInstance.update(delta);
if(this.started) {
this.currentInstance.update(delta);
}
}

keyboardEvent(event: KeyboardEvent) {
switch (event.code) {
case "Space":
this.restartGame();
return;
}
this.currentInstance.keyboardEvent(event);
}
gesture(direction: "up" | "down" | "left" | "right") {
this.currentInstance.gesture(direction);
}
}

export class GameInstance {
Expand All @@ -49,13 +47,28 @@ export class GameInstance {
this.gameSpeed += delta * INCREASE_SPEED_RATE;

const worldOffset = this.gameSpeed * delta;


const nextPickup = this.terrainManager.nextPickup(this.player.lane);

if(
nextPickup &&
nextPickup.offset - worldOffset < 0
&& nextPickup.height - 1 >= this.player.height
&& nextPickup.height - 2 <= this.player.height
) { // player hits the pickup in this update
this.terrainManager.removePickup(nextPickup.uuid);
this.score += nextPickup.points();
}

const nextObstacle = this.terrainManager.nextObstacle(this.player.lane);

if(nextObstacle && nextObstacle.offset - worldOffset < 0) { // player hits the obstacle in this update
switch(nextObstacle.type) {
case ObstacleType.Under:
// TODO: add rolling
if (!(this.player.runningState === RunningState.Rolling)) {
this.gameOver = true;
return;
}
break;
case ObstacleType.Over:
if(this.player.height < LOW_OBSTACLE_HEIGHT) {
Expand All @@ -66,14 +79,13 @@ export class GameInstance {
case ObstacleType.WagonStart:
// allows the player to go from a wagon or ramp to a wagon
// also let player go from nothing to a wagon, if they are high enough
if(!this.terrainInLane(this.player.lane) && this.player.height < 4) {
if(!this.terrainInLane(this.player.lane) && this.player.height < 4) {
this.gameOver = true;
return;
}
break;
case ObstacleType.Bar:
// TODO: add rolling
if(this.player.height < LOW_OBSTACLE_HEIGHT) {
if(this.player.height < LOW_OBSTACLE_HEIGHT && !(this.player.runningState === RunningState.Rolling)) {
this.gameOver = true;
return;
}
Expand Down Expand Up @@ -116,14 +128,21 @@ export class GameInstance {
this.terrainManager.addTerrain(newT, l);
}
const os = newSegment.obstacle[l];

for(const o of os) {
const newO = o.clone();
newO.offset += this.generatedFrontier;
this.terrainManager.addObstacle(newO, l);
}
const ps = newSegment.pickups[l];

for(const p of ps) {
const newP = p.clone();
newP.offset += this.generatedFrontier;
this.terrainManager.addPickup(newP, l);
}
}

this.terrainManager.thisDidChange();

this.generatedFrontier += newSegment.length;
Expand Down Expand Up @@ -154,21 +173,24 @@ export class GameInstance {
}
}

keyboardEvent(event: KeyboardEvent) {
gesture(event: "up" | "down" | "left" | "right") {
if (this.gameOver) {
return;
}

switch (event.code) {
case "ArrowUp":
switch (event) {
case "up":
this.player.jump();
break;
case "ArrowLeft":
case "left":
this.move("left");
break;
case "ArrowRight":
case "right":
this.move("right");
break;
case "down":
this.player.roll();
break;
default:
break;
}
Expand Down
35 changes: 35 additions & 0 deletions src/model/Pickup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { v4 as uuidv4 } from 'uuid';

// TODO: add more pickup types
export enum PickupType {
Notes,
}

export class Pickup {
type: PickupType;
height: number;
offset: number;
uuid: string;

constructor(type: PickupType, height: number, offset: number) {
this.type = type;
this.height = height;
this.offset = offset;
this.uuid = uuidv4();
}

update(worldOffset: number) {
this.offset -= worldOffset;
}

clone(): Pickup {
return new Pickup(this.type, this.height, this.offset);
}

points(): number {
switch (this.type) {
case PickupType.Notes:
return 100;
}
}
}
47 changes: 44 additions & 3 deletions src/model/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { Lane } from './Lane';
const GRAVITY: number = 9.82;
const JUMP_QUEUE_TIME: number = 0.5; // seconds
const AIR_RUN_TIME: number = 0.25; // seconds
const ROLLING_TIME: number = 0.85; // seconds

export enum RunningState {
OnGround,
AirBuffer,
MidAir,
Rolling,
AirRoll,
}

export default class Player {
Expand All @@ -17,13 +20,14 @@ export default class Player {
runningState: RunningState = RunningState.OnGround;
queuedJump: number = 0; // the number of seconds until a queued jump is discarded
airRunTime: number = 0; // the number of seconds until the fake ground is considered to have run out
rollingTime: number = 0; // the number of seconds left for a rolling action

constructor() {}

get onGround() {
return this.runningState === RunningState.OnGround || this.runningState === RunningState.AirBuffer;
return this.runningState === RunningState.OnGround || this.runningState === RunningState.AirBuffer || this.runningState == RunningState.Rolling;
}

update(delta: number, groundHeight: number) {
switch (this.runningState) {
case RunningState.OnGround:
Expand Down Expand Up @@ -51,13 +55,34 @@ export default class Player {
case RunningState.MidAir:
this.height += this.velY * delta;
this.velY -= GRAVITY * delta;

if (this.height <= groundHeight) {
this.height = groundHeight;
this.runningState = RunningState.OnGround;
this.velY = 0;
}
break;
case RunningState.Rolling:
this.rollingTime -= delta;
this.height = groundHeight;
if(this.rollingTime <= 0) {
this.runningState = RunningState.OnGround;
this.velY = 0;
}
if(this.velY > 0) {
this.runningState = RunningState.MidAir;
this.height += this.velY * delta;
}
break;
case RunningState.AirRoll:
this.height -= 3 * delta;
if (this.height <= groundHeight) {
this.height = groundHeight;
this.runningState = RunningState.Rolling;
this.rollingTime = ROLLING_TIME;
this.velY = 0;
}
break;
}

if (this.queuedJump > 0 && this.onGround) {
Expand All @@ -77,4 +102,20 @@ export default class Player {
this.queuedJump = JUMP_QUEUE_TIME;
}
}

roll() {
switch(this.runningState) {
case RunningState.OnGround:
this.runningState = RunningState.Rolling;
this.rollingTime = ROLLING_TIME;
break;
case RunningState.AirBuffer:
case RunningState.MidAir:
this.runningState = RunningState.AirRoll;
break;
case RunningState.Rolling:
case RunningState.AirRoll:
break; // cannot roll twice, and no queueing either
}
}
}
Loading

0 comments on commit 652bbdb

Please sign in to comment.