game state separated from world state
This commit is contained in:
parent
b3c7979f87
commit
c52458391d
|
|
@ -1,24 +1,12 @@
|
||||||
import { useLayoutEffect, useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useThree } from '@react-three/fiber';
|
|
||||||
import { Grid, OrbitControls } from '@react-three/drei';
|
import { Grid, OrbitControls } from '@react-three/drei';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import { state } from '../state';
|
import { state } from '../state';
|
||||||
import { SceneView } from './SceneView';
|
import { SceneView } from './SceneView';
|
||||||
import type { Pos3 } from '../types/3d';
|
|
||||||
import { type OrthographicCamera, type PerspectiveCamera } from 'three';
|
import { type OrthographicCamera, type PerspectiveCamera } from 'three';
|
||||||
|
import { CameraSync } from './tools/CameraSync';
|
||||||
|
|
||||||
const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
|
|
||||||
const { camera: threeCamera } = useThree();
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
threeCamera.position.set(camera.position[0], camera.position[1], camera.position[2]);
|
|
||||||
threeCamera.rotation.set(camera.look[0], camera.look[1], camera.look[2]);
|
|
||||||
threeCamera.updateProjectionMatrix();
|
|
||||||
});
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SceneEditorView = observer(function () {
|
export const SceneEditorView = observer(function () {
|
||||||
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,14 @@ export const ThreeView = observer(function () {
|
||||||
{
|
{
|
||||||
state.game
|
state.game
|
||||||
? <>
|
? <>
|
||||||
<button onClick={() => state.game!.stop()}><IconStop /></button>
|
<button onClick={() => state.stopGame()}><IconStop /></button>
|
||||||
{
|
{
|
||||||
state.game!.isPaused
|
state.game!.isPaused
|
||||||
? <button onClick={() => state.game!.resume()}><IconPlay /></button>
|
? <button onClick={() => state.game!.resume()}><IconPlay /></button>
|
||||||
: <button onClick={() => state.game!.pause()}><IconPause /></button>
|
: <button onClick={() => state.game!.pause()}><IconPause /></button>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
: <button onClick={() => state.world.play()}><IconPlay /></button>
|
: <button onClick={() => state.startGame()}><IconPlay /></button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useThree } from "@react-three/fiber";
|
||||||
|
import type { Pos3 } from "../../types/3d";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
|
export const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
|
||||||
|
const { camera: threeCamera } = useThree();
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
threeCamera.position.set(camera.position[0], camera.position[1], camera.position[2]);
|
||||||
|
threeCamera.rotation.set(camera.look[0], camera.look[1], camera.look[2]);
|
||||||
|
threeCamera.updateProjectionMatrix();
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
@ -14,9 +14,6 @@ export class WorldFactory {
|
||||||
},
|
},
|
||||||
objects: {},
|
objects: {},
|
||||||
},
|
},
|
||||||
state: {
|
|
||||||
playing: false,
|
|
||||||
},
|
|
||||||
gameRules: {
|
gameRules: {
|
||||||
gravity: true,
|
gravity: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,33 @@
|
||||||
import { makeAutoObservable } from "mobx";
|
import { makeAutoObservable, toJS } from "mobx";
|
||||||
import type { WorldState } from "./worldState";
|
import type { WorldState } from "./worldState";
|
||||||
import type { RunningGameState, Scene } from "../types";
|
import type { Game, Scene } from "../types";
|
||||||
import type { Pos3 } from "../types/3d";
|
import type { Pos3 } from "../types/3d";
|
||||||
import type { CameraProps } from "@react-three/fiber";
|
import type { CameraProps } from "@react-three/fiber";
|
||||||
|
import { clone } from "../utils";
|
||||||
|
import { state } from "./rootState";
|
||||||
|
|
||||||
export class GameState {
|
export class GameState {
|
||||||
private readonly world: WorldState;
|
private readonly world: WorldState;
|
||||||
|
|
||||||
|
public readonly data: Game;
|
||||||
|
|
||||||
constructor(world: WorldState) {
|
constructor(world: WorldState) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
|
this.data = {
|
||||||
|
paused: false,
|
||||||
|
time: 0,
|
||||||
|
scene: clone(toJS(this.world.data.initialScene)),
|
||||||
|
};
|
||||||
|
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get state(): RunningGameState {
|
|
||||||
return this.world.data.state as RunningGameState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isPaused(): boolean {
|
public get isPaused(): boolean {
|
||||||
return this.state.paused;
|
return this.data.paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get scene(): Scene {
|
public get scene(): Scene {
|
||||||
return this.state.scene;
|
return this.data.scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get camera(): Pos3 {
|
public get camera(): Pos3 {
|
||||||
|
|
@ -38,18 +44,17 @@ export class GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
public resume(): void {
|
public resume(): void {
|
||||||
this.state.paused = false;
|
this.data.paused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public pause(): void {
|
public pause(): void {
|
||||||
this.state.paused = true;
|
this.data.paused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop(): void {
|
public tick(deltaTime: number): void {
|
||||||
this.world.data.state = { playing: false };
|
if (this.isPaused)
|
||||||
}
|
return;
|
||||||
|
|
||||||
public tick(_deltaTime: number): void {
|
this.data.time += deltaTime;
|
||||||
//TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import { GameState } from "./gameState";
|
||||||
export class RootState {
|
export class RootState {
|
||||||
public readonly world = new WorldState();
|
public readonly world = new WorldState();
|
||||||
public readonly worldEditor: WorldEditorState;
|
public readonly worldEditor: WorldEditorState;
|
||||||
|
public game: GameState | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.worldEditor = new WorldEditorState(this.world);
|
this.worldEditor = new WorldEditorState(this.world);
|
||||||
|
|
@ -19,12 +20,22 @@ export class RootState {
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isGamePlaying(): boolean {
|
public get isGamePlaying(): boolean {
|
||||||
return this.world.data.state.playing;
|
return this.game !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get game(): GameState | undefined {
|
public startGame(): void {
|
||||||
if (this.isGamePlaying)
|
if (this.game)
|
||||||
return new GameState(this.world);
|
this.stopGame();
|
||||||
|
this.game = new GameState(this.world),
|
||||||
|
state.worldEditor.resetSelectedObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public stopGame(): void {
|
||||||
|
if (!this.game)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.game.pause();
|
||||||
|
this.game = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,6 @@ export class WorldState {
|
||||||
gameRules: {
|
gameRules: {
|
||||||
gravity: true,
|
gravity: true,
|
||||||
},
|
},
|
||||||
state: {
|
|
||||||
playing: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
console.log(objects);
|
console.log(objects);
|
||||||
state.worldEditor.resetSelectedObject();
|
state.worldEditor.resetSelectedObject();
|
||||||
|
|
@ -109,18 +106,4 @@ export class WorldState {
|
||||||
public getVoxelTypeById(id: string): VoxelType | undefined {
|
public getVoxelTypeById(id: string): VoxelType | undefined {
|
||||||
return this.data.voxelTypes[id];
|
return this.data.voxelTypes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
public play(): void {
|
|
||||||
if (state.game)
|
|
||||||
state.game.resume();
|
|
||||||
else {
|
|
||||||
this.data.state = {
|
|
||||||
playing: true,
|
|
||||||
paused: false,
|
|
||||||
time: 0,
|
|
||||||
scene: clone(this.data.initialScene),
|
|
||||||
}
|
|
||||||
state.worldEditor.resetSelectedObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import type { Scene } from "./scene";
|
||||||
|
|
||||||
|
export type Game = {
|
||||||
|
paused: boolean;
|
||||||
|
time: number;
|
||||||
|
scene: Scene;
|
||||||
|
}
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import type { Scene } from "./scene";
|
|
||||||
|
|
||||||
export type StoppedGameState = {
|
|
||||||
playing: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RunningGameState = {
|
|
||||||
playing: true;
|
|
||||||
paused: boolean;
|
|
||||||
time: number;
|
|
||||||
scene: Scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GameState = StoppedGameState | RunningGameState;
|
|
||||||
|
|
@ -2,7 +2,7 @@ export * from './object';
|
||||||
export * from './scene';
|
export * from './scene';
|
||||||
export * from './world';
|
export * from './world';
|
||||||
export * from './gameRules';
|
export * from './gameRules';
|
||||||
export * from './gameState';
|
export * from './game';
|
||||||
export * from './character';
|
export * from './character';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import type { GameRules } from "./gameRules";
|
import type { GameRules } from "./gameRules";
|
||||||
import type { GameState } from "./gameState";
|
|
||||||
import type { ObjectType } from "./object";
|
import type { ObjectType } from "./object";
|
||||||
import type { Scene } from "./scene";
|
import type { Scene } from "./scene";
|
||||||
import type { Pos3 } from "./3d";
|
import type { Pos3 } from "./3d";
|
||||||
|
|
@ -11,5 +10,4 @@ export type World = {
|
||||||
editorCamera: Pos3;
|
editorCamera: Pos3;
|
||||||
initialScene: Scene;
|
initialScene: Scene;
|
||||||
gameRules: GameRules;
|
gameRules: GameRules;
|
||||||
state: GameState;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue