mirror of
https://github.com/Aviortheking/games.git
synced 2025-04-23 11:22:09 +00:00
Started city builder like game
Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
79b74d16d5
commit
2143f9887e
13
package-lock.json
generated
13
package-lock.json
generated
@ -21,6 +21,7 @@
|
||||
"react-feather": "^2.0.9",
|
||||
"stylus": "^0.54.7",
|
||||
"stylus-loader": "^6.0.0",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.1.3",
|
||||
"webpack": "^5.37.1"
|
||||
},
|
||||
@ -8338,9 +8339,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
@ -15157,9 +15158,9 @@
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.21.0",
|
||||
|
@ -23,6 +23,7 @@
|
||||
"react-feather": "^2.0.9",
|
||||
"stylus": "^0.54.7",
|
||||
"stylus-loader": "^6.0.0",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.1.3",
|
||||
"webpack": "^5.37.1"
|
||||
},
|
||||
|
BIN
public/assets/city/background.png
Normal file
BIN
public/assets/city/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
@ -5,9 +5,9 @@ import Vector2D from '../Vector2D'
|
||||
export default class PointDebugger extends Component2D {
|
||||
public constructor(point: Vector2D, color = 'red') {
|
||||
super()
|
||||
this.scale = new Vector2D(.1, .1)
|
||||
this.scale = new Vector2D(1, 1)
|
||||
this.position = point
|
||||
console.log('Debugging point at location', point)
|
||||
// console.log('Debugging point at location', point)
|
||||
// this.origin = component.origin
|
||||
this.renderer = new RectRenderer(this, {material: color})
|
||||
}
|
||||
|
@ -25,10 +25,24 @@ export default class Vector2D {
|
||||
)
|
||||
}
|
||||
|
||||
public div(v: number): Vector2D {
|
||||
return new Vector2D(
|
||||
this.x / v,
|
||||
this.y / v
|
||||
)
|
||||
}
|
||||
|
||||
public isIn(topLeft: Vector2D, bottomRight: Vector2D): boolean {
|
||||
return this.x >= topLeft.x &&
|
||||
this.y >= topLeft.y &&
|
||||
this.x <= bottomRight.x &&
|
||||
this.y <= bottomRight.y
|
||||
}
|
||||
|
||||
public decimalCount(nDecimal: number) {
|
||||
return new Vector2D(
|
||||
parseFloat(this.x.toFixed(nDecimal)),
|
||||
parseFloat(this.y.toFixed(nDecimal))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ import Renderer from '.'
|
||||
|
||||
interface Params {
|
||||
material?: string | Asset
|
||||
stroke?: string
|
||||
stroke?: string | {color: string, width: number}
|
||||
}
|
||||
|
||||
export default class RectRenderer extends Renderer implements Params {
|
||||
|
||||
public material?: string | Asset
|
||||
public stroke?: string
|
||||
public stroke?: string | {color: string, width: number}
|
||||
|
||||
public constructor(component: Component2D, params?: Params) {
|
||||
super(component)
|
||||
@ -45,7 +45,12 @@ export default class RectRenderer extends Renderer implements Params {
|
||||
ctx.fillRect(...item)
|
||||
}
|
||||
if (this.stroke) {
|
||||
ctx.strokeStyle = this.stroke
|
||||
if (typeof this.stroke === 'string') {
|
||||
ctx.strokeStyle = this.stroke
|
||||
} else {
|
||||
ctx.strokeStyle = this.stroke.color
|
||||
ctx.lineWidth = this.stroke.width
|
||||
}
|
||||
ctx.strokeRect(...item)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ export default class TextRenderer extends Renderer {
|
||||
|
||||
public text?: string
|
||||
public size?: number
|
||||
public weight?: 'bold'
|
||||
public color?: string
|
||||
|
||||
public constructor(component: Component2D, params?: Params) {
|
||||
super(component)
|
||||
@ -31,10 +33,11 @@ export default class TextRenderer extends Renderer {
|
||||
|
||||
// console.log
|
||||
if (this.text) {
|
||||
ctx.fillStyle = 'black'
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.fillStyle = this.color ?? 'black'
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.textAlign = 'center'
|
||||
|
||||
ctx.font = `${size}px sans-serif`
|
||||
ctx.font = `${this.weight ? `${this.weight} ` : ''}${size + (this.size ?? 0)}px sans-serif`
|
||||
ctx.fillText(this.text, ...item)
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,11 @@ export default abstract class Renderer {
|
||||
|
||||
protected getPosition(): Vector2D {
|
||||
const ge = GameEngine.getGameEngine()
|
||||
const realPosition = ge.currentScene!.camera.topLeft.sum(this.component.position)
|
||||
const realPosition = ge.currentScene?.camera.topLeft.sum(this.component.position)
|
||||
if (!realPosition) {
|
||||
console.error('no camera?!?')
|
||||
return this.component.position
|
||||
}
|
||||
return new Vector2D(
|
||||
realPosition.x - this.component.scale.x / 2 - this.component.origin.x,
|
||||
realPosition.y - this.component.scale.y / 2 - this.component.origin.y
|
||||
|
@ -1,5 +1,4 @@
|
||||
import GameEngine from 'GameEngine'
|
||||
import AssetsManager from './Asset'
|
||||
import Camera from './Components/Camera'
|
||||
import Component2D, { ComponentState } from './Component2D'
|
||||
|
||||
@ -13,6 +12,7 @@ export default class Scene {
|
||||
|
||||
private components: Array<Component2D> = []
|
||||
private ge!: GameEngine
|
||||
private hasClickedComponent: number | undefined
|
||||
|
||||
|
||||
public constructor(sceneId: string) {
|
||||
@ -37,8 +37,9 @@ export default class Scene {
|
||||
}
|
||||
|
||||
public async update() {
|
||||
for (const component of this.components) {
|
||||
await this.updateComponent(component)
|
||||
for (let index = 0; index < this.components.length; index++) {
|
||||
const component = this.components[index];
|
||||
await this.updateComponent(component, index)
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +49,7 @@ export default class Scene {
|
||||
}
|
||||
}
|
||||
|
||||
private async updateComponent(v: Component2D) {
|
||||
private async updateComponent(v: Component2D, index: number) {
|
||||
const debug = v.debug
|
||||
if (debug) {
|
||||
console.log('Processing Component', v)
|
||||
@ -56,11 +57,20 @@ export default class Scene {
|
||||
const state: Partial<ComponentState> = {}
|
||||
// const width = (v.width() ?? 1) * this.ge.caseSize[0]
|
||||
// const height = (v.height() ?? 1) * this.ge.caseSize[1]
|
||||
if (v.collider && v.collider.type === 'click' && this.ge.cursor.isDown && !this.ge.cursor.wasDown) {
|
||||
if (v.collider && v.collider.type === 'click' && (this.hasClickedComponent === index || !this.hasClickedComponent)) {
|
||||
if (v.collider.pointColliding(this.ge.cursor.position, 'click')) {
|
||||
state.isColliding = 'click'
|
||||
if (this.ge.cursor.isDown && !this.ge.cursor.wasDown) {
|
||||
state.isColliding = 'click'
|
||||
this.hasClickedComponent = index
|
||||
} else if (this.ge.cursor.isDown) {
|
||||
state.isColliding = 'down'
|
||||
this.hasClickedComponent = index
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.hasClickedComponent === index && !state.isColliding) {
|
||||
this.hasClickedComponent = undefined
|
||||
}
|
||||
// if (v.pos) {
|
||||
// const ax = v.pos.x * this.ge.caseSize[0]
|
||||
// const ay = v.pos.y * this.ge.caseSize[1]
|
||||
|
@ -93,9 +93,11 @@ export default class GameEngine {
|
||||
}
|
||||
})
|
||||
document.addEventListener('mousedown', () => {
|
||||
console.log('cursor down')
|
||||
this.cursor.isDown = true
|
||||
})
|
||||
document.addEventListener('mouseup', () => {
|
||||
console.log('cursor up')
|
||||
this.cursor.isDown = false
|
||||
this.cursor.wasDown = false
|
||||
})
|
||||
|
27
src/games/city/Cursor.ts
Normal file
27
src/games/city/Cursor.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { stat } from 'fs'
|
||||
import GameEngine from 'GameEngine'
|
||||
import BoxCollider2D from 'GameEngine/2D/Collision/BoxCollider2D'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Component2D, { ComponentState } from 'GameEngine/Component2D'
|
||||
import Renderer from 'GameEngine/Renderer'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
|
||||
export default class Cursor extends Component2D {
|
||||
|
||||
public renderer: RectRenderer = new RectRenderer(this)
|
||||
public collider: BoxCollider2D = new BoxCollider2D(this, 'click')
|
||||
|
||||
public position: Vector2D = new Vector2D(0, 0)
|
||||
|
||||
public scale: Vector2D = new Vector2D(1, 1)
|
||||
|
||||
// public origin: Vector2D = new Vector2D(0.5, 0.5)
|
||||
|
||||
public update(state: ComponentState): void | Promise<void> {
|
||||
// state.mouseHovering
|
||||
|
||||
this.renderer.material = 'blue'
|
||||
const cursor = GameEngine.getGameEngine().cursor
|
||||
this.position = cursor.position
|
||||
}
|
||||
}
|
86
src/games/city/Space.ts
Normal file
86
src/games/city/Space.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { stat } from 'fs'
|
||||
import GameEngine from 'GameEngine'
|
||||
import BoxCollider2D from 'GameEngine/2D/Collision/BoxCollider2D'
|
||||
import ColliderDebugger from 'GameEngine/2D/Debug/ColliderDebugger'
|
||||
import PointDebugger from 'GameEngine/2D/Debug/PointDebugger'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Component2D, { ComponentState } from 'GameEngine/Component2D'
|
||||
import Renderer from 'GameEngine/Renderer'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import Cursor from './Cursor'
|
||||
import TextComponent from './TextComponent'
|
||||
|
||||
export default class Space extends Component2D {
|
||||
private static isOnCursor = false
|
||||
|
||||
public size = {
|
||||
width: 3, height: 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
public renderer: RectRenderer = new RectRenderer(this)
|
||||
public collider: BoxCollider2D = new BoxCollider2D(this, 'click')
|
||||
|
||||
public position: Vector2D = new Vector2D(0, 0)
|
||||
|
||||
public scale: Vector2D = new Vector2D(30, 10)
|
||||
|
||||
private posBeforeCursor: Vector2D | null = null
|
||||
public constructor(
|
||||
size: Vector2D,
|
||||
private cursor: Cursor,
|
||||
private placeableRects: Array<[Vector2D, Vector2D]>,
|
||||
private log: boolean = false
|
||||
) {
|
||||
super()
|
||||
// this.debug = true
|
||||
this.scale = size
|
||||
|
||||
const text = `${size.x}x${size.y}`
|
||||
this.childs = [
|
||||
new TextComponent(this, text, 'bold', 16, 'blue')
|
||||
]
|
||||
}
|
||||
|
||||
public update(state: ComponentState): void | Promise<void> {
|
||||
// state.mouseHovering
|
||||
const point = this.scale.div(2).sub(this.position)
|
||||
|
||||
this.renderer.material = 'white'
|
||||
this.renderer.stroke = {color: 'black', width: 3}
|
||||
const cursor = GameEngine.getGameEngine().cursor
|
||||
// if (this.log) console.log(point)
|
||||
if (state.isColliding === 'click' || state.isColliding === 'down') {
|
||||
this.renderer.stroke = {color: 'green', width: 3}
|
||||
if (!this.posBeforeCursor) {
|
||||
this.posBeforeCursor = this.position
|
||||
}
|
||||
Space.isOnCursor = true
|
||||
// console.log('follow cursor', cursor.position, this.position)
|
||||
this.position = cursor.position.decimalCount(0)
|
||||
let canPlace = false
|
||||
for (const placeableRect of this.placeableRects) {
|
||||
if (point.isIn(placeableRect[0], this.scale.sub(placeableRect[1]))) {
|
||||
canPlace = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!canPlace) this.renderer.stroke = {color: 'red', width: 3}
|
||||
else this.posBeforeCursor = this.position
|
||||
} else if (this.posBeforeCursor) {
|
||||
let canPlace = false
|
||||
for (const placeableRect of this.placeableRects) {
|
||||
if (point.isIn(placeableRect[0], this.scale.sub(placeableRect[1]))) {
|
||||
canPlace = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!canPlace) {
|
||||
this.position = this.posBeforeCursor
|
||||
}
|
||||
Space.isOnCursor = false
|
||||
this.posBeforeCursor = null
|
||||
}
|
||||
}
|
||||
}
|
31
src/games/city/TextComponent.ts
Normal file
31
src/games/city/TextComponent.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { stat } from 'fs'
|
||||
import GameEngine from 'GameEngine'
|
||||
import BoxCollider2D from 'GameEngine/2D/Collision/BoxCollider2D'
|
||||
import ColliderDebugger from 'GameEngine/2D/Debug/ColliderDebugger'
|
||||
import PointDebugger from 'GameEngine/2D/Debug/PointDebugger'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Component2D, { ComponentState } from 'GameEngine/Component2D'
|
||||
import Renderer from 'GameEngine/Renderer'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import TextRenderer from 'GameEngine/Renderer/TextRenderer'
|
||||
import Cursor from './Cursor'
|
||||
|
||||
export default class TextComponent extends Component2D {
|
||||
|
||||
|
||||
public renderer: TextRenderer = new TextRenderer(this)
|
||||
|
||||
// public position: Vector2D = new Vector2D(0, 0)
|
||||
|
||||
public constructor(private parent: Component2D, text: string, weight?: 'bold', size?: number, color?: string) {
|
||||
super()
|
||||
this.renderer.text = text
|
||||
this.renderer.weight = weight
|
||||
this.renderer.size = size
|
||||
this.renderer.color = color
|
||||
}
|
||||
|
||||
public update(state: ComponentState): void | Promise<void> {
|
||||
this.position = this.parent.position
|
||||
}
|
||||
}
|
91
src/pages/city/index.tsx
Normal file
91
src/pages/city/index.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import React from 'react'
|
||||
import { Text, Link } from '@dzeio/components'
|
||||
import GameEngine from 'GameEngine'
|
||||
import Scene from 'GameEngine/Scene'
|
||||
import Item from 'games/tictactoe/Item'
|
||||
import Line from 'games/tictactoe/Line'
|
||||
import Start from 'games/tictactoe/Menu/Start'
|
||||
import FPSCounter from 'GameEngine/Components/FPSCounter'
|
||||
import ComponentDebug from 'GameEngine/2D/Debug/ComponentDebug'
|
||||
import Tile from 'games/city/Tile'
|
||||
import Space from 'games/city/Space'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Cursor from 'games/city/Cursor'
|
||||
|
||||
export default class Snake extends React.PureComponent {
|
||||
|
||||
public async componentDidMount() {
|
||||
const ge = new GameEngine('#test', {
|
||||
caseCount: [208,104],
|
||||
debugColliders: true,
|
||||
goalFramerate: 60
|
||||
})
|
||||
const mainScene = new Scene('Menu')
|
||||
mainScene.camera.topLeft.x = 0
|
||||
mainScene.camera.topLeft.y = 0
|
||||
const cursor = new Cursor()
|
||||
|
||||
// for (let ch = 0; ch < 104; ch++) {
|
||||
// for (let cw = 0; cw < 208; cw++) {
|
||||
// mainScene.addComponent(
|
||||
// new Tile(cw, ch)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
const placeableRects: Array<[Vector2D, Vector2D]> = [
|
||||
// top rect
|
||||
[new Vector2D(0, 0), new Vector2D(208, 18)],
|
||||
|
||||
// left recet
|
||||
[new Vector2D(20, 28), new Vector2D(167, 79)],
|
||||
|
||||
// right rect
|
||||
[new Vector2D(178,28), new Vector2D(208, 79)],
|
||||
// bottom rect
|
||||
[new Vector2D(0, 89), new Vector2D(208, 104)],
|
||||
|
||||
|
||||
]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const width = i % 2 === 0 ? 20 : 10
|
||||
const height = i % 2 === 0 ? 10 : 20
|
||||
const baseX = width / 2 + 1
|
||||
const baseY = i % 2 === 0 ? 50 : 70
|
||||
|
||||
const it = new Space(new Vector2D(width, height), cursor, placeableRects)
|
||||
it.position.x = baseX
|
||||
it.position.y = baseY
|
||||
// it.debug = i === 0
|
||||
mainScene.addComponent(
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
// mainScene.addComponent(
|
||||
// // cursor,
|
||||
// new Space(new Vector2D(20, 10), cursor, placeableRects, true)
|
||||
// )
|
||||
|
||||
await ge.setScene(mainScene)
|
||||
ge.start()
|
||||
}
|
||||
|
||||
|
||||
public render = () => (
|
||||
<>
|
||||
<canvas id="test" width="2080" height="1040" style={{backgroundImage: 'url(\'/assets/city/background.png\')'}}></canvas>
|
||||
<Text>
|
||||
<span id="debug"></span>
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
|
||||
private randomInt(min: number, max: number): number {
|
||||
min = Math.ceil(min)
|
||||
max = Math.floor(max)
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user