diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..95ca4cd
Binary files /dev/null and b/.DS_Store differ
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..387d519
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,44 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+This is a Cocos Creator 3.8.7 2D game project focused on pathfinding mechanics. The game implements A* pathfinding with camera following behavior in a tile-based environment.
+
+## Project Structure
+
+- `assets/scripts/` - Core game logic TypeScript files
+- `assets/scenes/` - Cocos Creator scene files (main.scene)
+- `assets/resources/` - Game assets including tile maps and animations
+- `settings/v2/packages/` - Cocos Creator engine and project configuration
+
+## Key Components
+
+### GameController (`assets/scripts/GameController.ts`)
+- Main game logic handling player movement, pathfinding, and camera control
+- Implements A* pathfinding algorithm for grid-based navigation
+- Manages touch input for player movement commands
+- Contains hardcoded map data (5x10 grid with obstacles)
+
+### CameraFollow2D (`assets/scripts/CameraFollow2D.ts`)
+- Dedicated camera following component with smooth interpolation
+- Provides map boundary constraints to prevent camera from going outside map limits
+- Configurable offset and smoothing parameters
+
+### Manager (`assets/scripts/Manager.ts`)
+- Currently minimal/empty - likely intended for game state management
+
+## Engine Configuration
+
+- **Engine Version**: Cocos Creator 3.8.7
+- **Design Resolution**: 720x1334 (mobile portrait)
+- **Enabled Modules**: 2D rendering, UI, animation, audio, physics-2d, tiled-map, spine-3.8, dragon-bones
+- **TypeScript**: Uses strict mode disabled for development flexibility
+
+## Development Notes
+
+- The project uses a grid-based coordinate system with 32px grid size
+- Map data is currently hardcoded in GameController - consider externalizing for level design
+- Camera movement uses lerp interpolation with configurable smoothing
+- Pathfinding supports 4-directional movement (no diagonal)
\ No newline at end of file
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..972c9ff
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/resources/.DS_Store b/assets/resources/.DS_Store
index 345199e..e4001f0 100644
Binary files a/assets/resources/.DS_Store and b/assets/resources/.DS_Store differ
diff --git a/assets/resources/images.meta b/assets/resources/tileMap.meta
similarity index 100%
rename from assets/resources/images.meta
rename to assets/resources/tileMap.meta
diff --git a/assets/resources/images/Bg.png b/assets/resources/tileMap/Bg.png
similarity index 100%
rename from assets/resources/images/Bg.png
rename to assets/resources/tileMap/Bg.png
diff --git a/assets/resources/images/Bg.png.meta b/assets/resources/tileMap/Bg.png.meta
similarity index 100%
rename from assets/resources/images/Bg.png.meta
rename to assets/resources/tileMap/Bg.png.meta
diff --git a/assets/resources/tileMap/Walkable.tsx b/assets/resources/tileMap/Walkable.tsx
new file mode 100644
index 0000000..7d347a4
--- /dev/null
+++ b/assets/resources/tileMap/Walkable.tsx
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/assets/resources/tileMap/Walkable.tsx.meta b/assets/resources/tileMap/Walkable.tsx.meta
new file mode 100644
index 0000000..a0c2033
--- /dev/null
+++ b/assets/resources/tileMap/Walkable.tsx.meta
@@ -0,0 +1,11 @@
+{
+ "ver": "1.0.1",
+ "importer": "text",
+ "imported": true,
+ "uuid": "c9ba605b-b789-47ea-ba63-cedc94ceeab6",
+ "files": [
+ ".json"
+ ],
+ "subMetas": {},
+ "userData": {}
+}
diff --git a/assets/resources/tileMap/pixilart-drawing.png b/assets/resources/tileMap/pixilart-drawing.png
new file mode 100644
index 0000000..f59dee7
Binary files /dev/null and b/assets/resources/tileMap/pixilart-drawing.png differ
diff --git a/assets/resources/tileMap/pixilart-drawing.png.meta b/assets/resources/tileMap/pixilart-drawing.png.meta
new file mode 100644
index 0000000..f440aff
--- /dev/null
+++ b/assets/resources/tileMap/pixilart-drawing.png.meta
@@ -0,0 +1,134 @@
+{
+ "ver": "1.0.27",
+ "importer": "image",
+ "imported": true,
+ "uuid": "3d8bec36-4ba7-4722-8b83-076d1a818e2e",
+ "files": [
+ ".json",
+ ".png"
+ ],
+ "subMetas": {
+ "6c48a": {
+ "importer": "texture",
+ "uuid": "3d8bec36-4ba7-4722-8b83-076d1a818e2e@6c48a",
+ "displayName": "pixilart-drawing",
+ "id": "6c48a",
+ "name": "texture",
+ "userData": {
+ "wrapModeS": "clamp-to-edge",
+ "wrapModeT": "clamp-to-edge",
+ "imageUuidOrDatabaseUri": "3d8bec36-4ba7-4722-8b83-076d1a818e2e",
+ "isUuid": true,
+ "visible": false,
+ "minfilter": "linear",
+ "magfilter": "linear",
+ "mipfilter": "none",
+ "anisotropy": 0
+ },
+ "ver": "1.0.22",
+ "imported": true,
+ "files": [
+ ".json"
+ ],
+ "subMetas": {}
+ },
+ "f9941": {
+ "importer": "sprite-frame",
+ "uuid": "3d8bec36-4ba7-4722-8b83-076d1a818e2e@f9941",
+ "displayName": "pixilart-drawing",
+ "id": "f9941",
+ "name": "spriteFrame",
+ "userData": {
+ "trimThreshold": 1,
+ "rotated": false,
+ "offsetX": 0,
+ "offsetY": 0,
+ "trimX": 0,
+ "trimY": 0,
+ "width": 32,
+ "height": 32,
+ "rawWidth": 32,
+ "rawHeight": 32,
+ "borderTop": 0,
+ "borderBottom": 0,
+ "borderLeft": 0,
+ "borderRight": 0,
+ "packable": true,
+ "pixelsToUnit": 100,
+ "pivotX": 0.5,
+ "pivotY": 0.5,
+ "meshType": 0,
+ "vertices": {
+ "rawPosition": [
+ -16,
+ -16,
+ 0,
+ 16,
+ -16,
+ 0,
+ -16,
+ 16,
+ 0,
+ 16,
+ 16,
+ 0
+ ],
+ "indexes": [
+ 0,
+ 1,
+ 2,
+ 2,
+ 1,
+ 3
+ ],
+ "uv": [
+ 0,
+ 32,
+ 32,
+ 32,
+ 0,
+ 0,
+ 32,
+ 0
+ ],
+ "nuv": [
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1
+ ],
+ "minPos": [
+ -16,
+ -16,
+ 0
+ ],
+ "maxPos": [
+ 16,
+ 16,
+ 0
+ ]
+ },
+ "isUuid": true,
+ "imageUuidOrDatabaseUri": "3d8bec36-4ba7-4722-8b83-076d1a818e2e@6c48a",
+ "atlasUuid": "",
+ "trimType": "auto"
+ },
+ "ver": "1.0.12",
+ "imported": true,
+ "files": [
+ ".json"
+ ],
+ "subMetas": {}
+ }
+ },
+ "userData": {
+ "type": "sprite-frame",
+ "hasAlpha": true,
+ "fixAlphaTransparencyArtifacts": false,
+ "redirect": "3d8bec36-4ba7-4722-8b83-076d1a818e2e@6c48a"
+ }
+}
diff --git a/assets/resources/tileMap/tileMap.tmx b/assets/resources/tileMap/tileMap.tmx
new file mode 100644
index 0000000..6252d1a
--- /dev/null
+++ b/assets/resources/tileMap/tileMap.tmx
@@ -0,0 +1,91 @@
+
+
diff --git a/assets/resources/tileMap/tileMap.tmx.meta b/assets/resources/tileMap/tileMap.tmx.meta
new file mode 100644
index 0000000..0526325
--- /dev/null
+++ b/assets/resources/tileMap/tileMap.tmx.meta
@@ -0,0 +1,12 @@
+{
+ "ver": "1.0.2",
+ "importer": "tiled-map",
+ "imported": true,
+ "uuid": "dc1135fd-3f97-4e7d-84bd-b4fa0cc5bab2",
+ "files": [
+ ".json",
+ ".tmx"
+ ],
+ "subMetas": {},
+ "userData": {}
+}
diff --git a/assets/scenes/main.scene b/assets/scenes/main.scene
index b3feb6a..0ce1a83 100644
--- a/assets/scenes/main.scene
+++ b/assets/scenes/main.scene
@@ -52,7 +52,7 @@
},
"autoReleaseAssets": false,
"_globals": {
- "__id__": 18
+ "__id__": 27
},
"_id": "58132e64-0171-4c7f-89be-a2984ca7de6b"
},
@@ -71,26 +71,26 @@
{
"__id__": 9
},
+ {
+ "__id__": 18
+ },
{
"__id__": 6
},
{
- "__id__": 12
+ "__id__": 21
}
],
"_active": true,
"_components": [
{
- "__id__": 14
+ "__id__": 24
},
{
- "__id__": 15
+ "__id__": 25
},
{
- "__id__": 16
- },
- {
- "__id__": 17
+ "__id__": 26
}
],
"_prefab": null,
@@ -218,29 +218,27 @@
"_id": "63WIch3o5BEYRlXzTT0oWc"
},
{
- "__type__": "424a4ARzcRL9aX1sexNnCui",
+ "__type__": "5e229V75wdK255T4Dn05o4D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
- "_enabled": false,
+ "_enabled": true,
"__prefab": null,
"target": {
"__id__": 6
},
- "map": {
- "__id__": 2
- },
+ "followSpeed": 5,
"offset": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
- "z": 1000
+ "z": 10
},
- "smoothSpeed": 5,
- "_id": "beEkckKT1Ec5vv/5QnojSZ"
+ "smoothness": 0.1,
+ "_id": "f3ED2JS1JKurfMG53fWzmg"
},
{
"__type__": "cc.Node",
@@ -263,8 +261,8 @@
"_prefab": null,
"_lpos": {
"__type__": "cc.Vec3",
- "x": 134.715,
- "y": -841.498,
+ "x": -30.735,
+ "y": -71.207,
"z": 0
},
"_lrot": {
@@ -349,7 +347,303 @@
"_debugSlots": false,
"_enableBatch": false,
"loop": true,
- "_id": "f0I+7RM3NCv7gG/ZILUxjB"
+ "_id": "bfAE5dYjNG5JfiAUJyxBbf"
+ },
+ {
+ "__type__": "cc.Node",
+ "_name": "TiledMap",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "_parent": {
+ "__id__": 2
+ },
+ "_children": [
+ {
+ "__id__": 10
+ },
+ {
+ "__id__": 13
+ }
+ ],
+ "_active": true,
+ "_components": [
+ {
+ "__id__": 16
+ },
+ {
+ "__id__": 17
+ }
+ ],
+ "_prefab": null,
+ "_lpos": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_lrot": {
+ "__type__": "cc.Quat",
+ "x": 0,
+ "y": 0,
+ "z": 0,
+ "w": 1
+ },
+ "_lscale": {
+ "__type__": "cc.Vec3",
+ "x": 1,
+ "y": 1,
+ "z": 1
+ },
+ "_mobility": 0,
+ "_layer": 33554432,
+ "_euler": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_id": "05FcfqWLRCTLU2Pon+/5lM"
+ },
+ {
+ "__type__": "cc.Node",
+ "_name": "bg",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "_parent": {
+ "__id__": 9
+ },
+ "_children": [],
+ "_active": true,
+ "_components": [
+ {
+ "__id__": 11
+ },
+ {
+ "__id__": 12
+ }
+ ],
+ "_prefab": null,
+ "_lpos": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_lrot": {
+ "__type__": "cc.Quat",
+ "x": 0,
+ "y": 0,
+ "z": 0,
+ "w": 1
+ },
+ "_lscale": {
+ "__type__": "cc.Vec3",
+ "x": 1,
+ "y": 1,
+ "z": 1
+ },
+ "_mobility": 0,
+ "_layer": 33554432,
+ "_euler": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_id": "dcjO1Ab2tDIbg6VVr9YHdO"
+ },
+ {
+ "__type__": "cc.UITransform",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 10
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_contentSize": {
+ "__type__": "cc.Size",
+ "width": 1080,
+ "height": 2560
+ },
+ "_anchorPoint": {
+ "__type__": "cc.Vec2",
+ "x": 0.5,
+ "y": 0.5
+ },
+ "_id": "8aW0b4lGxCVbal8zQBKbyB"
+ },
+ {
+ "__type__": "cc.Sprite",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 10
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_customMaterial": null,
+ "_srcBlendFactor": 2,
+ "_dstBlendFactor": 4,
+ "_color": {
+ "__type__": "cc.Color",
+ "r": 255,
+ "g": 255,
+ "b": 255,
+ "a": 255
+ },
+ "_spriteFrame": {
+ "__uuid__": "361c5873-d797-420e-be65-81c5a8f91215@f9941",
+ "__expectedType__": "cc.SpriteFrame"
+ },
+ "_type": 0,
+ "_fillType": 0,
+ "_sizeMode": 0,
+ "_fillCenter": {
+ "__type__": "cc.Vec2",
+ "x": 0,
+ "y": 0
+ },
+ "_fillStart": 0,
+ "_fillRange": 0,
+ "_isTrimmedMode": true,
+ "_useGrayscale": false,
+ "_atlas": null,
+ "_id": "37daNZffxGSq/F3WV+F0Y5"
+ },
+ {
+ "__type__": "cc.Node",
+ "_name": "WalkableLayer",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "_parent": {
+ "__id__": 9
+ },
+ "_children": [],
+ "_active": true,
+ "_components": [
+ {
+ "__id__": 14
+ },
+ {
+ "__id__": 15
+ }
+ ],
+ "_prefab": null,
+ "_lpos": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_lrot": {
+ "__type__": "cc.Quat",
+ "x": 0,
+ "y": 0,
+ "z": 0,
+ "w": 1
+ },
+ "_lscale": {
+ "__type__": "cc.Vec3",
+ "x": 1,
+ "y": 1,
+ "z": 1
+ },
+ "_mobility": 0,
+ "_layer": 33554432,
+ "_euler": {
+ "__type__": "cc.Vec3",
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "_id": "23cuMKDVtPs71LOAe7QMgZ"
+ },
+ {
+ "__type__": "cc.UITransform",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 13
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_contentSize": {
+ "__type__": "cc.Size",
+ "width": 1056,
+ "height": 2560
+ },
+ "_anchorPoint": {
+ "__type__": "cc.Vec2",
+ "x": 0.5,
+ "y": 0.5
+ },
+ "_id": "e8IRovTjZNga4GC6wNHoIP"
+ },
+ {
+ "__type__": "cc.TiledLayer",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 13
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_customMaterial": null,
+ "_srcBlendFactor": 2,
+ "_dstBlendFactor": 4,
+ "_color": {
+ "__type__": "cc.Color",
+ "r": 255,
+ "g": 255,
+ "b": 255,
+ "a": 255
+ },
+ "_id": "30V2VyJEZCsKfZxqWiSZMJ"
+ },
+ {
+ "__type__": "cc.UITransform",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 9
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_contentSize": {
+ "__type__": "cc.Size",
+ "width": 1080,
+ "height": 2560
+ },
+ "_anchorPoint": {
+ "__type__": "cc.Vec2",
+ "x": 0.5,
+ "y": 0.5
+ },
+ "_id": "8c+XCMydFC9JqNb2Ygm4wJ"
+ },
+ {
+ "__type__": "cc.TiledMap",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 9
+ },
+ "_enabled": true,
+ "__prefab": null,
+ "_tmxFile": {
+ "__uuid__": "dc1135fd-3f97-4e7d-84bd-b4fa0cc5bab2",
+ "__expectedType__": "cc.TiledMapAsset"
+ },
+ "_enableCulling": true,
+ "cleanupImageCache": true,
+ "_id": "af4s2K0x1MH5srzPTEFKZE"
},
{
"__type__": "cc.Node",
@@ -360,13 +654,13 @@
"__id__": 2
},
"_children": [],
- "_active": true,
+ "_active": false,
"_components": [
{
- "__id__": 10
+ "__id__": 19
},
{
- "__id__": 11
+ "__id__": 20
}
],
"_prefab": null,
@@ -405,7 +699,7 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
- "__id__": 9
+ "__id__": 18
},
"_enabled": true,
"__prefab": null,
@@ -427,7 +721,7 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
- "__id__": 9
+ "__id__": 18
},
"_enabled": true,
"__prefab": null,
@@ -462,7 +756,7 @@
},
{
"__type__": "cc.Node",
- "_name": "node",
+ "_name": "PlayerController",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -472,14 +766,17 @@
"_active": true,
"_components": [
{
- "__id__": 13
+ "__id__": 22
+ },
+ {
+ "__id__": 23
}
],
"_prefab": null,
"_lpos": {
"__type__": "cc.Vec3",
- "x": -360,
- "y": -667,
+ "x": 0,
+ "y": 0,
"z": 0
},
"_lrot": {
@@ -496,38 +793,55 @@
"z": 1
},
"_mobility": 0,
- "_layer": 1073741824,
+ "_layer": 33554432,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
- "_id": "25oux7pUtPopnkQBFPwYfp"
+ "_id": "2dBJLKIX9BV4ynG/qUL0Kd"
},
{
- "__type__": "7d60dwmnZFI1LSM6LbxFuOw",
+ "__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
- "__id__": 12
+ "__id__": 21
},
"_enabled": true,
"__prefab": null,
- "camera": {
- "__id__": 4
+ "_contentSize": {
+ "__type__": "cc.Size",
+ "width": 100,
+ "height": 100
},
+ "_anchorPoint": {
+ "__type__": "cc.Vec2",
+ "x": 0.5,
+ "y": 0.5
+ },
+ "_id": "6bjIx8+UNMz47XvRycFFNU"
+ },
+ {
+ "__type__": "a1b2cPU5fZ4kKvN7xI0VniQ",
+ "_name": "",
+ "_objFlags": 0,
+ "__editorExtras__": {},
+ "node": {
+ "__id__": 21
+ },
+ "_enabled": true,
+ "__prefab": null,
"player": {
"__id__": 6
},
- "map": {
- "__id__": 9
+ "camera": {
+ "__id__": 4
},
- "gridSize": 10,
- "moveSpeed": 200,
- "camSmooth": 5,
- "_id": "0bSAKLAo1Hi6IeZn1HMR33"
+ "moveSpeed": 5,
+ "_id": "c1AuAU3IlKnLOzgk9vsBr4"
},
{
"__type__": "cc.UITransform",
@@ -541,8 +855,8 @@
"__prefab": null,
"_contentSize": {
"__type__": "cc.Size",
- "width": 1080,
- "height": 2560
+ "width": 720,
+ "height": 1334
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -575,14 +889,14 @@
"node": {
"__id__": 2
},
- "_enabled": true,
+ "_enabled": false,
"__prefab": null,
"_alignFlags": 45,
"_target": null,
- "_left": -180,
- "_right": -180,
- "_top": -613,
- "_bottom": -613,
+ "_left": 0,
+ "_right": 0,
+ "_top": 0,
+ "_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
@@ -597,70 +911,31 @@
"_lockFlags": 0,
"_id": "c5V1EV8IpMtrIvY1OE9t2u"
},
- {
- "__type__": "cc.Sprite",
- "_name": "",
- "_objFlags": 0,
- "__editorExtras__": {},
- "node": {
- "__id__": 2
- },
- "_enabled": false,
- "__prefab": null,
- "_customMaterial": null,
- "_srcBlendFactor": 2,
- "_dstBlendFactor": 4,
- "_color": {
- "__type__": "cc.Color",
- "r": 255,
- "g": 255,
- "b": 255,
- "a": 255
- },
- "_spriteFrame": {
- "__uuid__": "361c5873-d797-420e-be65-81c5a8f91215@f9941",
- "__expectedType__": "cc.SpriteFrame"
- },
- "_type": 0,
- "_fillType": 0,
- "_sizeMode": 1,
- "_fillCenter": {
- "__type__": "cc.Vec2",
- "x": 0,
- "y": 0
- },
- "_fillStart": 0,
- "_fillRange": 0,
- "_isTrimmedMode": true,
- "_useGrayscale": false,
- "_atlas": null,
- "_id": "3deM0mxjRKc5YJYLZwxQfb"
- },
{
"__type__": "cc.SceneGlobals",
"ambient": {
- "__id__": 19
+ "__id__": 28
},
"shadows": {
- "__id__": 20
+ "__id__": 29
},
"_skybox": {
- "__id__": 21
+ "__id__": 30
},
"fog": {
- "__id__": 22
+ "__id__": 31
},
"octree": {
- "__id__": 23
+ "__id__": 32
},
"skin": {
- "__id__": 24
+ "__id__": 33
},
"lightProbeInfo": {
- "__id__": 25
+ "__id__": 34
},
"postSettings": {
- "__id__": 26
+ "__id__": 35
},
"bakedWithStationaryMainLight": false,
"bakedWithHighpLightmap": false
diff --git a/assets/scripts/CameraFollow.ts b/assets/scripts/CameraFollow.ts
new file mode 100644
index 0000000..fd66858
--- /dev/null
+++ b/assets/scripts/CameraFollow.ts
@@ -0,0 +1,79 @@
+import { _decorator, Component, Node, Vec3, Camera } from 'cc';
+const { ccclass, property } = _decorator;
+
+@ccclass('CameraFollow')
+export class CameraFollow extends Component {
+
+ @property(Node)
+ target: Node | null = null; // 要跟随的目标(玩家)
+
+ @property({ range: [0.1, 10] })
+ followSpeed: number = 5.0; // 跟随速度
+
+ @property(Vec3)
+ offset: Vec3 = new Vec3(0, 0, 10); // 相机相对目标的偏移
+
+ @property({ range: [0, 1] })
+ smoothness: number = 0.1; // 平滑度,0为瞬间跟随,1为最慢跟随
+
+ private camera: Camera | null = null;
+
+ onLoad() {
+ // 获取相机组件
+ this.camera = this.getComponent(Camera);
+ if (!this.camera) {
+ console.error('CameraFollow: 未找到Camera组件');
+ }
+ }
+
+ start() {
+ if (this.target) {
+ // 初始化相机位置
+ const initialPos = this.target.position.clone();
+ initialPos.add(this.offset);
+ this.node.position = initialPos;
+ }
+ }
+
+ update(deltaTime: number) {
+ if (!this.target) return;
+
+ // 计算目标位置
+ const targetPosition = this.target.position.clone();
+ targetPosition.add(this.offset);
+
+ // 使用插值实现平滑跟随
+ const currentPosition = this.node.position;
+ const newPosition = new Vec3();
+
+ // 根据平滑度设置插值速度
+ const lerpFactor = Math.min(1.0, this.followSpeed * deltaTime * (1 - this.smoothness + 0.1));
+
+ Vec3.lerp(newPosition, currentPosition, targetPosition, lerpFactor);
+ this.node.position = newPosition;
+ }
+
+ // 设置跟随目标
+ setTarget(target: Node) {
+ this.target = target;
+ if (target) {
+ const initialPos = target.position.clone();
+ initialPos.add(this.offset);
+ this.node.position = initialPos;
+ }
+ }
+
+ // 设置偏移量
+ setOffset(offset: Vec3) {
+ this.offset = offset;
+ }
+
+ // 瞬间移动到目标位置
+ snapToTarget() {
+ if (!this.target) return;
+
+ const targetPosition = this.target.position.clone();
+ targetPosition.add(this.offset);
+ this.node.position = targetPosition;
+ }
+}
\ No newline at end of file
diff --git a/assets/scripts/CameraFollow2D.ts.meta b/assets/scripts/CameraFollow.ts.meta
similarity index 70%
rename from assets/scripts/CameraFollow2D.ts.meta
rename to assets/scripts/CameraFollow.ts.meta
index aaa2dd8..aaab43d 100644
--- a/assets/scripts/CameraFollow2D.ts.meta
+++ b/assets/scripts/CameraFollow.ts.meta
@@ -2,7 +2,7 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
- "uuid": "424a4011-cdc4-4bf5-a5f5-b1ec4d9c2ba2",
+ "uuid": "5e22957b-e707-4adb-9e53-e039f4e68e03",
"files": [],
"subMetas": {},
"userData": {}
diff --git a/assets/scripts/CameraFollow2D.ts b/assets/scripts/CameraFollow2D.ts
deleted file mode 100644
index b7e2ecd..0000000
--- a/assets/scripts/CameraFollow2D.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { _decorator, Component, Node, Vec3, UITransform, math } from 'cc';
-const { ccclass, property } = _decorator;
-
-@ccclass('CameraFollow2D')
-export class CameraFollow2D extends Component {
- @property(Node)
- target: Node = null!; // 要跟随的角色
-
- @property(Node)
- map: Node = null!; // 地图节点(需要有 UITransform 才能获取大小)
-
- @property
- offset: Vec3 = new Vec3(0, 0, 1000); // 相机与角色的偏移(Z 轴拉远)
-
- @property
- smoothSpeed: number = 5; // 平滑跟随速度,越大越快
-
- private _currentPos: Vec3 = new Vec3();
- private _mapSize: Vec3 = new Vec3();
-
- onLoad() {
- if (this.map) {
- const ui = this.map.getComponent(UITransform);
- if (ui) {
- this._mapSize.set(ui.width, ui.height, 0);
- }
- }
- }
-
- update(deltaTime: number) {
- if (!this.target) return;
-
- // 目标位置
- const targetPos = this.target.getWorldPosition();
- const desiredPos = new Vec3(
- targetPos.x + this.offset.x,
- targetPos.y + this.offset.y,
- this.offset.z // 相机保持固定 Z
- );
-
- // 平滑跟随
- Vec3.lerp(this._currentPos, this.node.getWorldPosition(), desiredPos, deltaTime * this.smoothSpeed);
-
- // 限制相机不超出地图
- if (this.map) {
- const halfW = this._mapSize.x / 2;
- const halfH = this._mapSize.y / 2;
-
- // 相机视口一半宽高(取 UITransform 尺寸的一半)
- const camUI = this.getComponent(UITransform);
- let viewW = camUI ? camUI.width / 2 : 0;
- let viewH = camUI ? camUI.height / 2 : 0;
-
- this._currentPos.x = math.clamp(this._currentPos.x, -halfW + viewW, halfW - viewW);
- this._currentPos.y = math.clamp(this._currentPos.y, -halfH + viewH, halfH - viewH);
- }
-
- // 更新相机位置
- this.node.setWorldPosition(this._currentPos);
- }
-}
diff --git a/assets/scripts/GameController.ts b/assets/scripts/GameController.ts
deleted file mode 100644
index 70d7691..0000000
--- a/assets/scripts/GameController.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { _decorator, Component, input, Input, EventTouch, Camera, Node, Vec3, UITransform, math } from 'cc';
-const { ccclass, property } = _decorator;
-
-interface NodeCell {
- x: number;
- y: number;
- g: number;
- h: number;
- f: number;
- parent?: NodeCell;
-}
-
-@ccclass('GameController')
-export class GameController extends Component {
- @property(Camera)
- camera: Camera = null!;
-
- @property(Node)
- player: Node = null!;
-
- @property(Node)
- map: Node = null!;
-
- @property
- gridSize: number = 32;
-
- @property
- moveSpeed: number = 200;
-
- @property
- camSmooth: number = 5;
-
- private mapData: number[][] = [
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
- [0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
- [0, 1, 0, 0, 0, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
- ];
-
- private path: Vec3[] = [];
- private camPos: Vec3 = new Vec3();
- private mapSize: Vec3 = new Vec3();
-
- onLoad() {
- input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
-
- // 获取地图大小
- const ui = this.map.getComponent(UITransform);
- if (ui) {
- this.mapSize.set(ui.width, ui.height, 0);
- }
- }
-
- onDestroy() {
- input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
- }
-
- // 点击地图 -> 计算寻路路径
- onTouchEnd(event: EventTouch) {
- if (!this.camera) return;
-
- const screenPos = event.getLocation();
- const worldPos = this.camera.screenToWorld(new Vec3(screenPos.x, screenPos.y, 0));
-
- const startCell = this.worldToCell(this.player.getWorldPosition());
- const endCell = this.worldToCell(worldPos);
-
- const cellPath = this.findPath(startCell, endCell);
- if (cellPath.length > 0) {
- this.path = cellPath.map(c => this.cellToWorld(c));
- }
- }
-
- update(deltaTime: number) {
- // === 角色移动 ===
- if (this.path.length > 0) {
- const pos = this.player.getWorldPosition();
- const target = this.path[0];
- const dir = target.subtract(pos);
- const dist = dir.length();
-
- if (dist < this.moveSpeed * deltaTime) {
- this.player.setWorldPosition(target);
- this.path.shift();
- } else {
- dir.normalize();
- const move = dir.multiplyScalar(this.moveSpeed * deltaTime);
- this.player.setWorldPosition(pos.add(move));
- }
- }
-
- // === 相机跟随 ===
- const targetPos = this.player.getWorldPosition();
- const desiredPos = new Vec3(targetPos.x, targetPos.y, 1000); // Z 拉远
- Vec3.lerp(this.camPos, this.camera.node.getWorldPosition(), desiredPos, deltaTime * this.camSmooth);
-
- // 限制相机范围
- const halfW = this.mapSize.x / 2;
- const halfH = this.mapSize.y / 2;
- const viewW = 400; // 你可以用实际相机视口宽高
- const viewH = 300;
-
- this.camPos.x = math.clamp(this.camPos.x, -halfW + viewW, halfW - viewW);
- this.camPos.y = math.clamp(this.camPos.y, -halfH + viewH, halfH - viewH);
-
- this.camera.node.setWorldPosition(this.camPos);
- }
-
- // ============ A* 寻路实现 ============
-
- worldToCell(worldPos: Vec3): { x: number, y: number } {
- return {
- x: Math.floor(worldPos.x / this.gridSize),
- y: Math.floor(worldPos.y / this.gridSize),
- };
- }
-
- cellToWorld(cell: { x: number, y: number }): Vec3 {
- return new Vec3(
- cell.x * this.gridSize + this.gridSize / 2,
- cell.y * this.gridSize + this.gridSize / 2,
- 0
- );
- }
-
- findPath(start: { x: number, y: number }, end: { x: number, y: number }): { x: number, y: number }[] {
- const open: NodeCell[] = [];
- const closed: boolean[][] = [];
- const rows = this.mapData.length;
- const cols = this.mapData[0].length;
-
- function heuristic(a: { x: number, y: number }, b: { x: number, y: number }) {
- return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
- }
-
- open.push({ x: start.x, y: start.y, g: 0, h: heuristic(start, end), f: 0 });
-
- while (open.length > 0) {
- open.sort((a, b) => (a.g + a.h) - (b.g + b.h));
- const current = open.shift()!;
- closed[current.y] = closed[current.y] || [];
- closed[current.y][current.x] = true;
-
- if (current.x === end.x && current.y === end.y) {
- const path: { x: number, y: number }[] = [];
- let node: NodeCell | undefined = current;
- while (node) {
- path.unshift({ x: node.x, y: node.y });
- node = node.parent;
- }
- return path;
- }
-
- const neighbors = [
- { x: current.x + 1, y: current.y },
- { x: current.x - 1, y: current.y },
- { x: current.x, y: current.y + 1 },
- { x: current.x, y: current.y - 1 },
- ];
-
- for (const n of neighbors) {
- if (n.x < 0 || n.y < 0 || n.y >= rows || n.x >= cols) continue;
- if (this.mapData[n.y][n.x] === 1) continue;
- if (closed[n.y]?.[n.x]) continue;
-
- const g = current.g + 1;
- const h = heuristic(n, end);
- const existing = open.find(o => o.x === n.x && o.y === n.y);
- if (!existing || g < existing.g) {
- open.push({ x: n.x, y: n.y, g, h, f: g + h, parent: current });
- }
- }
- }
- return [];
- }
-}
diff --git a/assets/scripts/PlayerController.ts b/assets/scripts/PlayerController.ts
new file mode 100644
index 0000000..6b2fa76
--- /dev/null
+++ b/assets/scripts/PlayerController.ts
@@ -0,0 +1,123 @@
+import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view } from 'cc';
+const { ccclass, property } = _decorator;
+
+@ccclass('PlayerController')
+export class PlayerController extends Component {
+
+ @property(Node)
+ player: Node | null = null; // 玩家节点
+
+ @property(Camera)
+ camera: Camera | null = null; // 主摄像机
+
+ @property({ range: [1, 20] })
+ moveSpeed: number = 5; // 移动速度
+
+ private isMoving: boolean = false;
+ private targetPosition: Vec3 = new Vec3();
+ private originalPosition: Vec3 = new Vec3();
+
+ onLoad() {
+ // 注册触摸事件
+ input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
+ }
+
+ onDestroy() {
+ // 移除触摸事件
+ input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
+ }
+
+ start() {
+ if (this.player) {
+ this.originalPosition.set(this.player.position);
+ }
+ }
+
+ private onTouchStart(event: EventTouch) {
+ if (!this.player || !this.camera) return;
+
+ // 获取触摸点的UI坐标
+ const touchLocation = event.getUILocation();
+
+ // 将UI坐标转换为世界坐标
+ const worldPos = this.screenToWorldPoint(touchLocation);
+
+ console.log(`触摸UI坐标: (${touchLocation.x}, ${touchLocation.y})`);
+ console.log(`转换后世界坐标: (${worldPos.x.toFixed(2)}, ${worldPos.y.toFixed(2)})`);
+
+ this.moveToPosition(worldPos);
+ }
+
+ private screenToWorldPoint(screenPos: { x: number, y: number }): Vec3 {
+ if (!this.camera) {
+ console.error('Camera未设置,无法进行坐标转换');
+ return new Vec3(screenPos.x, screenPos.y, 0);
+ }
+
+ // 获取可见区域大小
+ const visibleSize = view.getVisibleSize();
+
+ // 计算屏幕中心点
+ const centerX = visibleSize.width * 0.5;
+ const centerY = visibleSize.height * 0.5;
+
+ // 将屏幕坐标转换为以屏幕中心为原点的坐标
+ const normalizedX = screenPos.x - centerX;
+ const normalizedY = screenPos.y - centerY;
+
+ // 考虑相机的位置偏移
+ const cameraPos = this.camera.node.position;
+
+ // 计算世界坐标
+ const worldX = normalizedX + cameraPos.x;
+ const worldY = normalizedY + cameraPos.y;
+
+ return new Vec3(worldX, worldY, 0);
+ }
+
+
+ private moveToPosition(worldPos: Vec3) {
+ if (!this.player) return;
+
+ // 设置目标位置(保持Z轴不变)
+ this.targetPosition.set(worldPos.x, worldPos.y, this.player.position.z);
+ this.isMoving = true;
+
+ console.log(`移动目标: (${worldPos.x.toFixed(2)}, ${worldPos.y.toFixed(2)})`);
+ }
+
+ update(deltaTime: number) {
+ if (!this.isMoving || !this.player) return;
+
+ const currentPos = this.player.position;
+ const distance = Vec3.distance(currentPos, this.targetPosition);
+
+ // 如果距离很小,直接到达目标位置
+ if (distance < 0.1) {
+ this.player.position = this.targetPosition.clone();
+ this.isMoving = false;
+ console.log('到达目标位置');
+ return;
+ }
+
+ // 计算移动方向
+ const direction = new Vec3();
+ Vec3.subtract(direction, this.targetPosition, currentPos);
+ direction.normalize();
+
+ // 计算这一帧应该移动的距离
+ const moveDistance = this.moveSpeed * deltaTime * 100; // 增加移动速度倍数
+
+ // 如果剩余距离小于这一帧要移动的距离,直接到达目标
+ if (distance <= moveDistance) {
+ this.player.position = this.targetPosition.clone();
+ this.isMoving = false;
+ console.log('到达目标位置');
+ } else {
+ // 正常移动
+ const newPosition = new Vec3();
+ Vec3.scaleAndAdd(newPosition, currentPos, direction, moveDistance);
+ this.player.position = newPosition;
+ }
+ }
+}
\ No newline at end of file
diff --git a/assets/scripts/GameController.ts.meta b/assets/scripts/PlayerController.ts.meta
similarity index 70%
rename from assets/scripts/GameController.ts.meta
rename to assets/scripts/PlayerController.ts.meta
index a01fbd4..19a8328 100644
--- a/assets/scripts/GameController.ts.meta
+++ b/assets/scripts/PlayerController.ts.meta
@@ -2,7 +2,7 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
- "uuid": "7d60dc26-9d91-48d4-b48c-e8b6f116e3b0",
+ "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"files": [],
"subMetas": {},
"userData": {}
diff --git a/settings/v2/packages/cocos-service.json b/settings/v2/packages/cocos-service.json
new file mode 100644
index 0000000..938c84f
--- /dev/null
+++ b/settings/v2/packages/cocos-service.json
@@ -0,0 +1,23 @@
+{
+ "__version__": "3.0.9",
+ "game": {
+ "name": "未知游戏",
+ "app_id": "UNKNOW",
+ "c_id": "0"
+ },
+ "appConfigMaps": [
+ {
+ "app_id": "UNKNOW",
+ "config_id": "41a441"
+ }
+ ],
+ "configs": [
+ {
+ "app_id": "UNKNOW",
+ "config_id": "41a441",
+ "config_name": "Default",
+ "config_remarks": "",
+ "services": []
+ }
+ ]
+}