Map object position as tile coordinates, map objects correctly snap to each tile now when moving, and fixed bug of placing objects over eachother on the same tile

This commit is contained in:
Andrei 2025-02-03 14:53:46 -06:00
parent 90d7252784
commit 841ec0f3df
7 changed files with 65 additions and 63 deletions

View File

@ -5,15 +5,19 @@
<script setup lang="ts"> <script setup lang="ts">
import type { PlacedMapObject, TextureData } from '@/application/types' import type { PlacedMapObject, TextureData } from '@/application/types'
import { loadTexture } from '@/composables/gameComposable' import { loadTexture } from '@/composables/gameComposable'
import { calculateIsometricDepth } from '@/composables/mapComposable' import { calculateIsometricDepth, tileToWorldXY } from '@/composables/mapComposable'
import { useGameStore } from '@/stores/gameStore' import { useGameStore } from '@/stores/gameStore'
import { Image, useScene } from 'phavuer' import { Image, useScene } from 'phavuer'
import { computed, onMounted } from 'vue' import { computed, onMounted } from 'vue'
import config from '@/application/config' import config from '@/application/config'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
const props = defineProps<{ const props = defineProps<{
placedMapObject: PlacedMapObject placedMapObject: PlacedMapObject,
tileMap: Tilemap
tileMapLayer: TilemapLayer
}>() }>()
const gameStore = useGameStore() const gameStore = useGameStore()
@ -32,12 +36,11 @@ const imageProps = computed(() => ({
})) }))
function calculateObjectPlacement(mapObj: PlacedMapObject) : {x: number; y: number} { function calculateObjectPlacement(mapObj: PlacedMapObject) : {x: number; y: number} {
let position = { x: mapObj.positionX, y: mapObj.positionY } let position = tileToWorldXY(props.tileMapLayer, mapObj.positionX, mapObj.positionY)
let halfTileWidth = config.tile_size.width/2
let halfTileHeight = config.tile_size.height/2
return { return {
x: position.x-mapObj.mapObject.frameWidth/2, x: position.worldPositionX-mapObj.mapObject.frameWidth/2,
y: position.y-mapObj.mapObject.frameHeight/2-halfTileHeight y: position.worldPositionY-mapObj.mapObject.frameHeight/2+config.tile_size.height
} }
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<MapTiles ref="mapTiles" @tileMap:create="tileMap = $event" /> <MapTiles ref="mapTiles" v-if="tileMap && tileMapLayer" :tileMapLayer :tileMap @tileMap:create="tileMap = $event" />
<PlacedMapObjects ref="mapObjects" v-if="tileMap" :tileMap /> <PlacedMapObjects ref="mapObjects" v-if="tileMap && tileMapLayer" :tileMapLayer :tileMap />
<MapEventTiles ref="eventTiles" v-if="tileMap" :tileMap /> <MapEventTiles ref="eventTiles" v-if="tileMap && tileMapLayer" :tileMapLayer :tileMap />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -11,8 +11,11 @@ import PlacedMapObjects from '@/components/gameMaster/mapEditor/mapPartials/Plac
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { useScene } from 'phavuer' import { useScene } from 'phavuer'
import { onBeforeUnmount, onMounted, onUnmounted, shallowRef, useTemplateRef } from 'vue' import { onBeforeUnmount, onMounted, onUnmounted, shallowRef, useTemplateRef } from 'vue'
import { createTileLayer, createTileMap } from '@/composables/mapComposable'
const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>() const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>()
const tileMapLayer = shallowRef<Phaser.Tilemaps.TilemapLayer>()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const scene = useScene() const scene = useScene()
@ -72,6 +75,10 @@ function handlePointerUp(pointer: Phaser.Input.Pointer) {
} }
onMounted(() => { onMounted(() => {
tileMap.value = createTileMap(scene, mapEditor.currentMap.value!)
mapTiles.value?.$emit('tileMap:create', tileMap.value)
tileMapLayer.value = createTileLayer(tileMap.value, mapEditor.currentMap.value)
addEventListener('keydown', handleKeyDown) addEventListener('keydown', handleKeyDown)
scene.input.on(Phaser.Input.Events.POINTER_DOWN, handlePointerDown) scene.input.on(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
@ -79,6 +86,12 @@ onMounted(() => {
}) })
onUnmounted(() => { onUnmounted(() => {
if (tileMap.value) {
tileMap.value.destroyLayer('tiles')
tileMap.value.removeAllLayers()
tileMap.value.destroy()
}
scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown) scene.input.off(Phaser.Input.Events.POINTER_DOWN, handlePointerDown)
scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp) scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp)

View File

@ -3,7 +3,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { MapEventTileType, type MapEventTile, type Map as MapT } from '@/application/types' import { MapEventTileType, type MapEventTile, type Map as MapT, type UUID } from '@/application/types'
import { uuidv4 } from '@/application/utilities' import { uuidv4 } from '@/application/utilities'
import { getTile, tileToWorldX, tileToWorldY } from '@/composables/mapComposable' import { getTile, tileToWorldX, tileToWorldY } from '@/composables/mapComposable'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
@ -42,7 +42,7 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
if (mapEditor.drawMode.value === 'teleport' && !mapEditor.teleportSettings.value.toMapId) return if (mapEditor.drawMode.value === 'teleport' && !mapEditor.teleportSettings.value.toMapId) return
const newEventTile = { const newEventTile = {
id: uuidv4(), id: uuidv4() as UUID,
mapId: map.id, mapId: map.id,
map: map.id, map: map.id,
type: mapEditor.drawMode.value === 'blocking tile' ? MapEventTileType.BLOCK : MapEventTileType.TELEPORT, type: mapEditor.drawMode.value === 'blocking tile' ? MapEventTileType.BLOCK : MapEventTileType.TELEPORT,
@ -56,7 +56,7 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
toPositionY: mapEditor.teleportSettings.value.toPositionY, toPositionY: mapEditor.teleportSettings.value.toPositionY,
toRotation: mapEditor.teleportSettings.value.toRotation toRotation: mapEditor.teleportSettings.value.toRotation
} }
: undefined : {}
} }
map.mapEventTiles.push(newEventTile) map.mapEventTiles.push(newEventTile)

View File

@ -1,5 +1,5 @@
<template> <template>
<Controls v-if="tileLayer" :layer="tileLayer" :depth="0" /> <Controls v-if="props.tileMapLayer" :layer="props.tileMapLayer" :depth="0" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -16,11 +16,13 @@ const scene = useScene()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
const tileStorage = new TileStorage() const tileStorage = new TileStorage()
const tileMap = shallowRef<Phaser.Tilemaps.Tilemap>()
const tileLayer = shallowRef<Phaser.Tilemaps.TilemapLayer>()
defineExpose({ handlePointer, finalizeCommand, undo, redo }) defineExpose({ handlePointer, finalizeCommand, undo, redo })
const props = defineProps<{
tileMap: Phaser.Tilemaps.Tilemap
tileMapLayer: Phaser.Tilemaps.TilemapLayer
}>()
class EditorCommand { class EditorCommand {
public operation: 'draw' | 'erase' = 'draw' public operation: 'draw' | 'erase' = 'draw'
public tileName: string = "blank_tile" public tileName: string = "blank_tile"
@ -46,14 +48,12 @@ function pencil(pointer: Phaser.Input.Pointer) {
// Check if there is a selected tile // Check if there is a selected tile
if (!mapEditor.selectedTile.value) return if (!mapEditor.selectedTile.value) return
if (!tileMap.value || !tileLayer.value) return
// Check if there is a tile // Check if there is a tile
const tile = getTile(tileLayer.value, pointer.worldX, pointer.worldY) const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
if (!tile) return if (!tile) return
// Place tile // Place tile
placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, mapEditor.selectedTile.value) placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, mapEditor.selectedTile.value)
createCommandUpdate(tile.x, tile.y, mapEditor.selectedTile.value, 'draw') createCommandUpdate(tile.x, tile.y, mapEditor.selectedTile.value, 'draw')
@ -65,14 +65,12 @@ function eraser(pointer: Phaser.Input.Pointer) {
let map = mapEditor.currentMap.value let map = mapEditor.currentMap.value
if (!map) return if (!map) return
if (!tileMap.value || !tileLayer.value) return
// Check if there is a tile // Check if there is a tile
const tile = getTile(tileLayer.value, pointer.worldX, pointer.worldY) const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
if (!tile) return if (!tile) return
// Place tile // Place tile
placeTile(tileMap.value, tileLayer.value, tile.x, tile.y, 'blank_tile') placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, 'blank_tile')
createCommandUpdate(tile.x, tile.y, 'blank_tile', 'erase') createCommandUpdate(tile.x, tile.y, 'blank_tile', 'erase')
@ -81,10 +79,9 @@ function eraser(pointer: Phaser.Input.Pointer) {
} }
function paint(pointer: Phaser.Input.Pointer) { function paint(pointer: Phaser.Input.Pointer) {
if (!tileMap.value || !tileLayer.value) return
// Set new tileArray with selected tile // Set new tileArray with selected tile
const tileArray = createTileArray(tileMap.value.width, tileMap.value.height, mapEditor.selectedTile.value) const tileArray = createTileArray(props.tileMap.width, props.tileMap.height, mapEditor.selectedTile.value)
setLayerTiles(tileMap.value, tileLayer.value, tileArray) setLayerTiles(props.tileMap, props.tileMapLayer, tileArray)
// Adjust mapEditorStore.map.tiles // Adjust mapEditorStore.map.tiles
if (mapEditor.currentMap.value) { if (mapEditor.currentMap.value) {
@ -97,10 +94,8 @@ function tilePicker(pointer: Phaser.Input.Pointer) {
let map = mapEditor.currentMap.value let map = mapEditor.currentMap.value
if (!map) return if (!map) return
if (!tileMap.value || !tileLayer.value) return
// Check if there is a tile // Check if there is a tile
const tile = getTile(tileLayer.value, pointer.worldX, pointer.worldY) const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
if (!tile) return if (!tile) return
// Select the tile // Select the tile
@ -108,8 +103,6 @@ function tilePicker(pointer: Phaser.Input.Pointer) {
} }
function handlePointer(pointer: Phaser.Input.Pointer) { function handlePointer(pointer: Phaser.Input.Pointer) {
if (!tileMap.value || !tileLayer.value) return
// Check if left mouse button is pressed // Check if left mouse button is pressed
if (!pointer.isDown && pointer.button === 0) return if (!pointer.isDown && pointer.button === 0) return
@ -187,13 +180,13 @@ function applyCommands(tiles: string[][], ...commands: EditorCommand[]): string[
} }
function updateMapTiles() { function updateMapTiles() {
if (!tileMap.value || !tileLayer.value || !mapEditor.currentMap.value) return if (!mapEditor.currentMap.value) return
let indexedCommands = commandStack.slice(0, commandIndex.value) let indexedCommands = commandStack.slice(0, commandIndex.value)
let modifiedTiles = applyCommands(originTiles, ...indexedCommands) let modifiedTiles = applyCommands(originTiles, ...indexedCommands)
//replaceTiles(mapEditor.currentMap.value.tiles, layer, tileMap.value.width, tileMap.value.height) //replaceTiles(mapEditor.currentMap.value.tiles, layer, tileMap.value.width, tileMap.value.height)
setLayerTiles(tileMap.value, tileLayer.value, modifiedTiles) setLayerTiles(props.tileMap, props.tileMapLayer, modifiedTiles)
mapEditor.currentMap.value.tiles = modifiedTiles mapEditor.currentMap.value.tiles = modifiedTiles
} }
@ -205,10 +198,10 @@ function cloneArray(arr: any[]) : any[] {
watch( watch(
() => mapEditor.shouldClearTiles, () => mapEditor.shouldClearTiles,
(shouldClear) => { (shouldClear) => {
if (shouldClear && mapEditor.currentMap.value && tileMap.value && tileLayer.value) { if (shouldClear && mapEditor.currentMap.value) {
const blankTiles = createTileArray(tileLayer.value.width, tileLayer.value.height, 'blank_tile') const blankTiles = createTileArray(props.tileMapLayer.width, props.tileMapLayer.height, 'blank_tile')
setLayerTiles(tileMap.value, tileLayer.value, blankTiles) setLayerTiles(props.tileMap, props.tileMapLayer, blankTiles)
replaceTiles(mapEditor.currentMap.value.tiles, blankTiles, tileLayer.value.width, tileLayer.value.height) replaceTiles(mapEditor.currentMap.value.tiles, blankTiles, props.tileMapLayer.width, props.tileMapLayer.height)
mapEditor.resetClearTilesFlag() mapEditor.resetClearTilesFlag()
} }
} }
@ -230,20 +223,9 @@ onMounted(async () => {
const mapState = mapEditor.currentMap.value const mapState = mapEditor.currentMap.value
//Clone //Clone
originTiles = cloneArray(mapEditor.currentMap.value.tiles) originTiles = cloneArray(mapState.tiles)
tileMap.value = createTileMap(scene, mapEditor.currentMap.value) setLayerTiles(props.tileMap, props.tileMapLayer, mapState.tiles)
emit('tileMap:create', tileMap.value)
tileLayer.value = createTileLayer(tileMap.value, mapEditor.currentMap.value)
setLayerTiles(tileMap.value, tileLayer.value, mapState.tiles)
}) })
onUnmounted(() => {
if (tileMap.value) {
tileMap.value.destroyLayer('tiles')
tileMap.value.removeAllLayers()
tileMap.value.destroy()
}
})
</script> </script>

View File

@ -1,10 +1,10 @@
<template> <template>
<SelectedPlacedMapObjectComponent v-if="mapEditor.selectedPlacedObject.value" :placedMapObject="mapEditor.selectedPlacedObject.value" @move="moveMapObject" @rotate="rotatePlacedMapObject" @delete="deletePlacedMapObject" /> <SelectedPlacedMapObjectComponent v-if="mapEditor.selectedPlacedObject.value" :placedMapObject="mapEditor.selectedPlacedObject.value" @move="moveMapObject" @rotate="rotatePlacedMapObject" @delete="deletePlacedMapObject" />
<PlacedMapObject v-for="placedMapObject in mapEditor.currentMap.value?.placedMapObjects" :placedMapObject @pointerdown="clickPlacedMapObject(placedMapObject)" /> <PlacedMapObject :tileMapLayer :tileMap v-for="placedMapObject in mapEditor.currentMap.value?.placedMapObjects" :placedMapObject @pointerdown="clickPlacedMapObject(placedMapObject)" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { Map as MapT, PlacedMapObject as PlacedMapObjectT } from '@/application/types' import type { Map as MapT, PlacedMapObject as PlacedMapObjectT, UUID } from '@/application/types'
import { uuidv4 } from '@/application/utilities' import { uuidv4 } from '@/application/utilities'
import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue' import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue'
import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue' import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue'
@ -13,6 +13,7 @@ import { useScene } from 'phavuer'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { getTile, tileToWorldX, tileToWorldY } from '@/composables/mapComposable' import { getTile, tileToWorldX, tileToWorldY } from '@/composables/mapComposable'
import Tilemap = Phaser.Tilemaps.Tilemap import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer
const scene = useScene() const scene = useScene()
const mapEditor = useMapEditorComposable() const mapEditor = useMapEditorComposable()
@ -20,7 +21,8 @@ const mapEditor = useMapEditorComposable()
defineExpose({ handlePointer }) defineExpose({ handlePointer })
const props = defineProps<{ const props = defineProps<{
tileMap: Tilemap tileMap: Tilemap,
tileMapLayer: TilemapLayer
}>() }>()
function pencil(pointer: Phaser.Input.Pointer, map: MapT) { function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
@ -33,13 +35,13 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
if (!mapEditor.selectedMapObject.value) return if (!mapEditor.selectedMapObject.value) return
const newPlacedMapObject: PlacedMapObjectT = { const newPlacedMapObject: PlacedMapObjectT = {
id: uuidv4(), id: uuidv4() as UUID,
depth: 0, depth: 0,
map: map, map: map,
mapObject: mapEditor.selectedMapObject.value, mapObject: mapEditor.selectedMapObject.value,
isRotated: false, isRotated: false,
positionX: tileToWorldX(props.tileMap, tile.x, tile.y), positionX: tile.x,
positionY: tileToWorldY(props.tileMap, tile.x, tile.y) positionY: tile.y
} }
// Add new object to mapObjects // Add new object to mapObjects
@ -75,9 +77,11 @@ function moveMapObject(id: string, map: MapT) {
function handlePointerMove(pointer: Phaser.Input.Pointer) { function handlePointerMove(pointer: Phaser.Input.Pointer) {
if (!mapEditor.movingPlacedObject.value) return if (!mapEditor.movingPlacedObject.value) return
const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
if (!tile) return
mapEditor.movingPlacedObject.value.positionX = pointer.worldX mapEditor.movingPlacedObject.value.positionX = tile.x
mapEditor.movingPlacedObject.value.positionY = pointer.worldY mapEditor.movingPlacedObject.value.positionY = tile.y
} }
scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove) scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
@ -103,7 +107,7 @@ function rotatePlacedMapObject(id: string, map: MapT) {
} }
function deletePlacedMapObject(id: string, map: MapT) { function deletePlacedMapObject(id: string, map: MapT) {
let mapE = mapEditor.currentMap.value let mapE = mapEditor.currentMap.value!
mapE.placedMapObjects = map.placedMapObjects.filter((object) => object.id !== id) mapE.placedMapObjects = map.placedMapObjects.filter((object) => object.id !== id)
mapEditor.selectedPlacedObject.value = null mapEditor.selectedPlacedObject.value = null
} }

View File

@ -105,7 +105,7 @@ let selectEraserOpen = ref(false)
let tileListShown = ref(false) let tileListShown = ref(false)
let mapObjectListShown = ref(false) let mapObjectListShown = ref(false)
let checkboxValue = ref(false) let checkboxValue = ref<Boolean>(false)
defineExpose({ tileListShown, mapObjectListShown }) defineExpose({ tileListShown, mapObjectListShown })

View File

@ -4,7 +4,7 @@
<Scene name="main" @preload="preloadScene"> <Scene name="main" @preload="preloadScene">
<div v-if="!isLoaded" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-3xl font-ui">Loading...</div> <div v-if="!isLoaded" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-3xl font-ui">Loading...</div>
<div v-else> <div v-else>
<Map :key="mapEditor.currentMap.value?.id" /> <Map v-if="mapEditor.currentMap.value" :key="mapEditor.currentMap.value?.id" />
<Toolbar <Toolbar
ref="toolbar" ref="toolbar"
@save="save" @save="save"