Commit 29f47b46 authored by haiyoucuv's avatar haiyoucuv

init

parent b193474d
This diff is collapsed.
[
{
"__type__": "cc.Prefab",
"_name": "AISnake",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "AISnake",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 20
},
{
"__id__": 22
}
],
"_prefab": {
"__id__": 24
},
"_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": 1,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "Head",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [
{
"__id__": 3
}
],
"_active": true,
"_components": [
{
"__id__": 11
},
{
"__id__": 13
},
{
"__id__": 15
},
{
"__id__": 17
}
],
"_prefab": {
"__id__": 19
},
"_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": 1,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "范围",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 2
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 4
},
{
"__id__": 6
},
{
"__id__": 8
}
],
"_prefab": {
"__id__": 10
},
"_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": 1,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 5
},
"_contentSize": {
"__type__": "cc.Size",
"width": 100,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "0fac9VpfVO24GEVbqN0rDr"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 7
},
"enabledContactListener": true,
"bullet": false,
"awakeOnLoad": true,
"_group": 2,
"_type": 1,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
"_angularDamping": 0,
"_linearVelocity": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_angularVelocity": 0,
"_fixedRotation": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "06tgmn5lJOSrxGT9RzZ/yH"
},
{
"__type__": "cc.CircleCollider2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"tag": 0,
"_group": 2,
"_density": 1,
"_sensor": true,
"_friction": 0.2,
"_restitution": 0,
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_radius": 100,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "01JFesoj9K0ZY+F0UNJzML"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "11XLaTdr5Np4aokfvlatgx",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 12
},
"_contentSize": {
"__type__": "cc.Size",
"width": 100,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "5deDNWrJxPhZTvijInr7WK"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 14
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": null,
"_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": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "a9l0+kQJxKPZS/WlExP1lc"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 16
},
"enabledContactListener": true,
"bullet": false,
"awakeOnLoad": true,
"_group": 2,
"_type": 1,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
"_angularDamping": 0,
"_linearVelocity": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_angularVelocity": 0,
"_fixedRotation": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "58PN+1WP9JUYhjArC/UslP"
},
{
"__type__": "cc.CircleCollider2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 18
},
"tag": 0,
"_group": 2,
"_density": 1,
"_sensor": true,
"_friction": 0.2,
"_restitution": 0,
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_radius": 50,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "bdxTN8nPZA/q7Zd4BUQkQm"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "99DwWvaTlMx7bgFz0iHbqY",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 21
},
"_contentSize": {
"__type__": "cc.Size",
"width": 0,
"height": 0
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "a7ATZENxxM9opDu0hsckS7"
},
{
"__type__": "9a4204/Rl9HW5pomKW0LWdO",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 23
},
"head": {
"__id__": 2
},
"bodyPrefab": {
"__uuid__": "8c4f22c7-20c7-4868-88ea-dedb8004999f",
"__expectedType__": "cc.Prefab"
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "85va8m2SpDPq8Fj2dbJkmE"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "ee8O/qSSVCI71mLCUiPZ14",
"instance": null,
"targetOverrides": null
}
]
\ No newline at end of file
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "f7e4cff9-e50b-4028-b58e-f258871093ca",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "AISnake"
}
}
......@@ -205,7 +205,7 @@
"bullet": false,
"awakeOnLoad": true,
"_group": 8,
"_type": 2,
"_type": 1,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
......
......@@ -151,7 +151,7 @@
"bullet": false,
"awakeOnLoad": true,
"_group": 16,
"_type": 2,
"_type": 1,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
......
......@@ -136,7 +136,7 @@
"fileId": "00ZbcQtopI2JWAiMHxJkn+"
},
{
"__type__": "cc.CircleCollider2D",
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
......@@ -147,26 +147,30 @@
"__prefab": {
"__id__": 7
},
"tag": 0,
"enabledContactListener": true,
"bullet": false,
"awakeOnLoad": true,
"_group": 4,
"_density": 1,
"_sensor": true,
"_friction": 0.2,
"_restitution": 0,
"_offset": {
"_type": 1,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
"_angularDamping": 0,
"_linearVelocity": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_radius": 15,
"_angularVelocity": 0,
"_fixedRotation": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "bbd/hzdzVJ1rG0lXtNgF7s"
"fileId": "2d/O9bV5VIyqWQb88vJlpL"
},
{
"__type__": "cc.RigidBody2D",
"__type__": "cc.CircleCollider2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
......@@ -177,27 +181,23 @@
"__prefab": {
"__id__": 9
},
"enabledContactListener": true,
"bullet": false,
"awakeOnLoad": true,
"tag": 0,
"_group": 4,
"_type": 2,
"_allowSleep": true,
"_gravityScale": 1,
"_linearDamping": 0,
"_angularDamping": 0,
"_linearVelocity": {
"_density": 1,
"_sensor": true,
"_friction": 0.2,
"_restitution": 0,
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_angularVelocity": 0,
"_fixedRotation": false,
"_radius": 15,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "2d/O9bV5VIyqWQb88vJlpL"
"fileId": "bbd/hzdzVJ1rG0lXtNgF7s"
},
{
"__type__": "cc.PrefabInfo",
......
import { _decorator, math, v3, Vec3 } from "cc";
import { Snake } from "./Snake";
import { DirectionType } from "./Enums";
import { Global } from "./Global";
import { MainGame } from "./MainGame";
const { ccclass, property } = _decorator;
@ccclass("AISnake")
export class AISnake extends Snake {
// AI难度等级
@property({
range: [1, 5],
tooltip: "AI难度(1-5)"
})
private difficulty: number = 5;
private v = v3();
private direction: DirectionType = DirectionType.DEFAULT;
private directionSpeed: number = 0;
// AI行为相关参数
private readonly VIEW_DISTANCE: number = 300; // 视野范围
private readonly AVOID_DISTANCE: number = 100; // 躲避距离
private targetFood: Vec3 = null;
private dangerSnake: Snake = null;
private behaviorTimer: number = 0;
private readonly BEHAVIOR_UPDATE_TIME = 0.5; // 行为更新间隔
// 难度相关参数
private get difficultyParams() {
return {
reactionTime: math.lerp(0.8, 0.2, (this.difficulty - 1) / 4),
viewDistance: math.lerp(200, 400, (this.difficulty - 1) / 4),
decisionAccuracy: math.lerp(0.4, 0.9, (this.difficulty - 1) / 4),
aggressiveness: math.lerp(0.2, 0.8, (this.difficulty - 1) / 4)
};
}
onUpdate(dt: number) {
if (!this.isLife) return;
this.behaviorTimer += dt;
if (this.behaviorTimer >= this.difficultyParams.reactionTime) {
this.behaviorTimer = 0;
this.updateAIBehavior();
}
// 预判断位置
this.v = this.getNewPos(
this.head.angle,
dt,
this.head.getPosition(),
this.speed * 20
);
this.handleBoundaryAvoidance();
this.executeCurrentBehavior(dt);
super.onUpdate(dt);
}
private updateAIBehavior() {
const params = this.difficultyParams;
// 检测危险
this.detectThreats();
// 如果检测到危险,优先躲避
if (this.dangerSnake && math.random() < params.decisionAccuracy) {
this.avoidThreat();
return;
}
// 寻找最近的食物
this.findNearestFood();
// 根据难度决定是否追逐食物
if (this.targetFood && math.random() < params.decisionAccuracy) {
this.chaseFood();
} else {
this.randomMove();
}
// 根据难度决定是否加速
if (this.shouldSpeedUp()) {
this.isFast = true;
} else {
this.isFast = false;
}
}
private detectThreats() {
const params = this.difficultyParams;
const myPos = this.head.getPosition();
let nearestDanger = null;
let minDistance = params.viewDistance;
// 检测其他蛇
const allSnakes = [...MainGame.ins.animalNode.children];
allSnakes.push(MainGame.ins.player.node);
for (const snakeNode of allSnakes) {
const snake = snakeNode.getComponent(Snake);
if (snake === this) continue;
const distance = Vec3.distance(myPos, snake.head.getPosition());
if (distance < minDistance && snake.getSnakeLen() >= this.getSnakeLen()) {
minDistance = distance;
nearestDanger = snake;
}
}
this.dangerSnake = nearestDanger;
}
private findNearestFood() {
const params = this.difficultyParams;
const myPos = this.head.getPosition();
let nearestFood = null;
let minDistance = params.viewDistance;
// 获取视野范围内的食物
MainGame.ins.fondManger.node.children.forEach(food => {
const distance = Vec3.distance(myPos, food.getPosition());
if (distance < minDistance) {
minDistance = distance;
nearestFood = food.getPosition();
}
});
this.targetFood = nearestFood;
}
private avoidThreat() {
const myPos = this.head.getPosition();
const dangerPos = this.dangerSnake.head.getPosition();
// 计算逃离角度
const angle = math.toDegree(Math.atan2(
myPos.y - dangerPos.y,
myPos.x - dangerPos.x
));
this.head.angle = angle;
this.isFast = true;
}
private chaseFood() {
const myPos = this.head.getPosition();
// 计算追逐角度
const angle = math.toDegree(Math.atan2(
this.targetFood.y - myPos.y,
this.targetFood.x - myPos.x
));
// 平滑转向
const angleDiff = this.head.angle - angle;
this.head.angle += math.clamp(angleDiff, -3, 3);
}
private handleBoundaryAvoidance() {
const halfWidth = Global.MAP_WIDTH / 2;
const halfHeight = Global.MAP_HIGHT / 2;
if (this.v.x <= -halfWidth || this.v.x >= halfWidth ||
this.v.y <= -halfHeight || this.v.y >= halfHeight) {
const angleAbs = Math.abs(this.head.angle % 180);
this.direction = DirectionType.DEFAULT;
// 根据难度调整转向的精确度
const turnAngle = math.lerp(
math.randomRangeInt(90, 180),
135,
this.difficultyParams.decisionAccuracy
);
if (angleAbs > 90) {
this.head.angle += turnAngle;
} else {
this.head.angle -= turnAngle;
}
}
}
private randomMove() {
if (math.randomRangeInt(0, 11) == 0) {
this.direction = math.randomRangeInt(0, 3);
this.directionSpeed = math.randomRangeInt(0, 6);
}
if (this.direction == DirectionType.LEFT) {
this.head.angle += this.directionSpeed;
} else if (this.direction == DirectionType.RIGHT) {
this.head.angle -= this.directionSpeed;
}
}
private shouldSpeedUp(): boolean {
const params = this.difficultyParams;
// 在以下情况下加速:
// 1. 追逐食物且距离适中
// 2. 逃离危险
// 3. 随机因素(基于激进度)
if (this.dangerSnake) return true;
if (this.targetFood) {
const distance = Vec3.distance(this.head.getPosition(), this.targetFood);
if (distance < this.VIEW_DISTANCE / 2) return true;
}
return math.random() < params.aggressiveness;
}
private executeCurrentBehavior(dt: number) {
// 可以在这里添加更多行为逻辑
// 比如:追逐较小的蛇、与同类保持距离等
}
// 设置AI难度
setDifficulty(level: number) {
this.difficulty = math.clamp(level, 1, 5);
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c9d3c00d-0519-4dc0-9e9f-103327cfcc08",
"uuid": "9a420e3f-465f-475b-9a68-98a5b42d674e",
"files": [],
"subMetas": {},
"userData": {}
......
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "4fa63f7f-07d2-4949-b7aa-3f9827a3e8ce",
"files": [],
"subMetas": {},
"userData": {}
}
import { _decorator } from "cc";
import { BuffType, BuffState } from "./BuffType";
import { Snake } from "../Snake";
const { ccclass, property } = _decorator;
@ccclass("BaseBuff")
export class BaseBuff {
protected type: BuffType = BuffType.NONE;
protected duration: number = 0;
protected remainTime: number = 0;
protected value: number = 0;
protected state: BuffState = BuffState.INACTIVE;
protected target: Snake = null;
constructor(type: BuffType, duration: number, value: number) {
this.type = type;
this.duration = duration;
this.remainTime = duration;
this.value = value;
}
// 激活Buff
activate(target: Snake): void {
this.target = target;
this.state = BuffState.ACTIVE;
this.onActivate();
}
// 更新Buff状态
update(dt: number): boolean {
if (this.state !== BuffState.ACTIVE) return false;
this.remainTime -= dt;
this.onUpdate(dt);
if (this.remainTime <= 0) {
this.deactivate();
return false;
}
return true;
}
// 停用Buff
deactivate(): void {
this.state = BuffState.FINISHED;
this.onDeactivate();
}
// 获取剩余时间
getRemainTime(): number {
return Math.max(0, this.remainTime);
}
// 获取Buff类型
getType(): BuffType {
return this.type;
}
// 获取Buff状态
getState(): BuffState {
return this.state;
}
// 获取Buff值
getValue(): number {
return this.value;
}
// 以下是子类需要实现的方法
protected onActivate(): void { }
protected onUpdate(dt: number): void { }
protected onDeactivate(): void { }
}
\ No newline at end of file
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "448e8ec0-47bb-488e-b80b-e52e93c7136f",
"files": [],
"subMetas": {},
"userData": {}
}
import { _decorator } from "cc";
import { BaseBuff } from "./BaseBuff";
import { BuffType } from "./BuffType";
import { Snake } from "../Snake";
import { ShieldBuff, SpeedUpBuff, MagnetBuff } from "./Buffs";
const { ccclass } = _decorator;
@ccclass("BuffManager")
export class BuffManager {
private buffs: Map<BuffType, BaseBuff> = new Map();
private target: Snake = null;
constructor(target: Snake) {
this.target = target;
}
// 添加Buff
addBuff(buff: BaseBuff): void {
const type = buff.getType();
// 如果已存在同类型Buff,先移除
if (this.buffs.has(type)) {
this.removeBuff(type);
}
this.buffs.set(type, buff);
buff.activate(this.target);
}
// 移除指定类型的Buff
removeBuff(type: BuffType): void {
const buff = this.buffs.get(type);
if (buff) {
buff.deactivate();
this.buffs.delete(type);
}
}
// 更新所有Buff
update(dt: number): void {
this.buffs.forEach((buff, type) => {
if (!buff.update(dt)) {
this.buffs.delete(type);
}
});
}
// 检查是否有某个Buff
hasBuff(type: BuffType): boolean {
return this.buffs.has(type);
}
// 获取Buff
getBuff(type: BuffType): BaseBuff | null {
return this.buffs.get(type) || null;
}
// 清除所有Buff
clearAll(): void {
this.buffs.forEach(buff => buff.deactivate());
this.buffs.clear();
}
// 获取所有激活的Buff
getActiveBuffs(): BaseBuff[] {
return Array.from(this.buffs.values());
}
// 创建Buff的便捷方法
createBuff(type: BuffType, duration?: number, value?: number): BaseBuff {
switch (type) {
case BuffType.SHIELD:
return new ShieldBuff(duration);
case BuffType.SPEED_UP:
return new SpeedUpBuff(duration, value);
case BuffType.MAGNET:
return new MagnetBuff(duration, value);
default:
return null;
}
}
}
\ No newline at end of file
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "1224d146-f247-4925-ba1c-eb35147e5b8f",
"files": [],
"subMetas": {},
"userData": {}
}
export enum BuffType {
NONE = 0,
SHIELD = 1, // 护盾
MAGNET = 2, // 磁铁
SPEED_UP = 3, // 加速
DOUBLE_SCORE = 4, // 双倍积分
INVINCIBLE = 5, // 无敌
FROZEN = 6, // 冰冻
// ... 可以继续添加更多类型
}
// Buff的一些通用状态
export enum BuffState {
INACTIVE = 0, // 未激活
ACTIVE = 1, // 激活中
FINISHED = 2 // 已结束
}
\ No newline at end of file
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "de21d110-11a6-4935-a4b4-1898a9fe6101",
"files": [],
"subMetas": {},
"userData": {}
}
import { BaseBuff } from "./BaseBuff";
import { BuffType } from "./BuffType";
// 护盾Buff
export class ShieldBuff extends BaseBuff {
constructor(duration: number = 10) {
super(BuffType.SHIELD, duration, 1);
}
protected onActivate(): void {
this.target.setInvincible(true);
}
protected onDeactivate(): void {
this.target.setInvincible(false);
}
}
// 加速Buff
export class SpeedUpBuff extends BaseBuff {
constructor(duration: number = 8, accPercent: number = 1.5) {
super(BuffType.SPEED_UP, duration, accPercent);
}
protected onActivate(): void {
this.target.setSpeedMultiplier(this.value);
}
protected onDeactivate(): void {
this.target.setSpeedMultiplier(1);
}
}
// 磁铁Buff
export class MagnetBuff extends BaseBuff {
constructor(duration: number = 15, range: number = 100) {
super(BuffType.MAGNET, duration, range);
}
protected onUpdate(dt: number): void {
// 在这里实现磁铁效果的逻辑
// 例如:吸引范围内的食物
}
}
\ No newline at end of file
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "aa216ae1-29cc-4cdc-bb17-86f9e1701160",
"files": [],
"subMetas": {},
"userData": {}
}
import { _decorator } from "cc";
import { FlagType } from "./Enums";
const { ccclass } = _decorator;
interface FlagInfo {
type: FlagType;
duration: number; // 持续时间
remainTime: number; // 剩余时间
value: number; // 效果值
}
@ccclass("FlagSystem")
export class FlagSystem {
private flags: Map<FlagType, FlagInfo> = new Map();
// Flag 默认配置
private static readonly DEFAULT_CONFIG: { [key in FlagType]?: { duration: number, value: number } } = {
[FlagType.SHIELD]: { duration: 10, value: 1 }, // 护盾10秒
[FlagType.MAGNET]: { duration: 15, value: 100 }, // 磁铁15秒,吸收范围100
[FlagType.SPEED_UP]: { duration: 8, value: 1.5 }, // 加速8秒,速度1.5倍
[FlagType.DOUBLE_SCORE]: { duration: 12, value: 2 }, // 双倍积分12秒
};
// 添加一个Flag
addFlag(type: FlagType, duration?: number, value?: number) {
const config = FlagSystem.DEFAULT_CONFIG[type];
if (!config) return;
const flagInfo: FlagInfo = {
type,
duration: duration || config.duration,
remainTime: duration || config.duration,
value: value || config.value
};
this.flags.set(type, flagInfo);
}
// 移除一个Flag
removeFlag(type: FlagType) {
this.flags.delete(type);
}
// 检查Flag是否存在且有效
hasFlag(type: FlagType): boolean {
return this.flags.has(type) && this.flags.get(type).remainTime > 0;
}
// 获取Flag的值
getFlagValue(type: FlagType): number {
const flag = this.flags.get(type);
return flag && flag.remainTime > 0 ? flag.value : 0;
}
// 获取Flag的剩余时间
getFlagRemainTime(type: FlagType): number {
const flag = this.flags.get(type);
return flag ? flag.remainTime : 0;
}
// 更新所有Flag状态
update(dt: number) {
this.flags.forEach((flag, type) => {
flag.remainTime -= dt;
if (flag.remainTime <= 0) {
this.flags.delete(type);
}
});
}
// 清除所有Flag
clearAll() {
this.flags.clear();
}
// 获取所有活动的Flag
getActiveFlags(): FlagInfo[] {
return Array.from(this.flags.values()).filter(flag => flag.remainTime > 0);
}
}
\ No newline at end of file
......@@ -2,7 +2,6 @@ import { _decorator, Enum, SpriteFrame, math, v3, Sprite, Collider2D, Component,
import { PoolManager } from "./PoolManager";
import { FoodType } from "./Enums";
import { Global } from "./Global";
import { MainGame } from "./MainGame";
import { executePreFrame, getItemGenerator } from "../../Utils/ExecutePreFrame";
const { ccclass, property } = _decorator;
......@@ -12,11 +11,11 @@ const { ccclass, property } = _decorator;
*/
@ccclass("Item")
class Item {
@property({ type: FoodType })
type: FoodType = FoodType.FOOD;
@property({ type: FoodType })
type: FoodType = FoodType.FOOD;
@property(SpriteFrame)
sp: SpriteFrame = null;
@property(SpriteFrame)
sp: SpriteFrame = null;
}
/**
......@@ -25,78 +24,93 @@ class Item {
@ccclass("FondManger")
export class FondManger extends Component {
@property({ type: [Item] })
private foods: Item[] = [];
@property(Prefab)
private foodPrefab: Prefab = null;
/**
* 获取当前食物数量
*/
getFoodSum() {
return this.node.children.length || 0;
}
/**
* 初始化食物
*/
async init(count: number = 100) {
await this.initFond(count);
}
/**
* 设置食物
*/
addFood(x?: number, y?: number) {
// 如果没有指定位置,随机生成位置
if (!x) {
x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
@property({ type: [Item] })
private foods: Item[] = [];
@property(Prefab)
private foodPrefab: Prefab = null;
maxFood: number = 100;
/**
* 获取当前食物数量
*/
getFoodSum() {
return this.node.children.length || 0;
}
/**
* 初始化食物
*/
async init(maxFood: number = 100) {
this.maxFood = maxFood;
await this.initFond(maxFood);
this.schedule(this.checkFood, 1);
}
if (!y) {
y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
/**
* 检查食物
*/
checkFood() {
for (let i = this.getFoodSum(); i < this.maxFood; i++) {
this.addFood();
}
}
// 从对象池获取食物节点
const node = PoolManager.instance.getNode(this.foodPrefab);
/**
* 设置食物
*/
addFood(x?: number, y?: number) {
// 如果没有指定位置,随机生成位置
if (!x) {
x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
}
if (!y) {
y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
}
// 随机选择食物类型
const index = math.randomRangeInt(0, this.foods.length);
// 从对象池获取食物节点
const node = PoolManager.instance.getNode(this.foodPrefab);
this.node.addChild(node);
// 设置食物属性
node.angle = math.randomRange(0, 360);
node.setScale(1, 1);
node.setPosition(x, y);
node.getComponent(Sprite).spriteFrame = this.foods[index].sp;
// 随机选择食物类型
const index = math.randomRangeInt(0, this.foods.length);
// 设置碰撞类型
const collider = node.getComponent(Collider2D);
if (collider) {
collider.tag = this.foods[index].type;
this.node.addChild(node);
// 设置食物属性
node.angle = math.randomRange(0, 360);
node.setScale(1, 1);
node.setPosition(x, y);
node.getComponent(Sprite).spriteFrame = this.foods[index].sp;
// 设置碰撞类型
const collider = node.getComponent(Collider2D);
if (collider) {
collider.tag = this.foods[index].type;
}
node.active = true;
}
node.active = true;
}
/**
* 初始化单个食物
*/
initItem = (_: number) => {
// 随机生成位置
const x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
const y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
this.addFood(x, y);
}
/**
* 初始化食物
*/
async initFond(count: number) {
await executePreFrame(getItemGenerator(count, this.initItem), 1, this);
}
/**
* 初始化单个食物
*/
initItem = (_: number) => {
// 随机生成位置
const x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
const y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
this.addFood(x, y);
};
/**
* 初始化食物
*/
async initFond(count: number) {
await executePreFrame(getItemGenerator(count, this.initItem), 1, this);
}
}
......@@ -2,10 +2,6 @@ import {
_decorator,
Camera,
director,
EventKeyboard,
input,
Input,
KeyCode,
Label,
math,
Node,
......@@ -14,17 +10,18 @@ import {
UITransform,
} from "cc";
import { Joystick } from "./Joystick";
import { Snake } from "./Snake";
import { FastBtn } from "./FastBtn";
import { Animal } from "./Animal";
import { FondManger } from "./FondManger";
import { Config } from "./Config";
import { Global } from "./Global";
import { EBodyTag, Events, GameState } from "./Enums";
import { Events, GameState } from "./Enums";
import { PoolManager } from "./PoolManager";
import { showToast } from "../../../Module/UIFast";
import Scene from "../../../Module/Scene";
import { executePreFrame, getItemGenerator } from "../../Utils/ExecutePreFrame";
import { Player } from "./Player";
import { AISnake } from "./AISnake";
const { ccclass, property } = _decorator;
......@@ -61,14 +58,14 @@ export class MainGame extends Scene {
@property(UITransform)
private uiBg: UITransform = null;
@property(Snake)
private snake: Snake = null;
@property(Player)
player: Player = null;
@property(FondManger)
fondManger: FondManger = null;
@property(Node)
private animalNode: Node = null;
animalNode: Node = null;
@property(Label)
private LTips: Label = null;
......@@ -90,6 +87,12 @@ export class MainGame extends Scene {
MainGame._ins = this;
PhysicsSystem2D.instance.enable = true;
// PhysicsSystem2D.instance.gravity = math.Vec2.ZERO;
// PhysicsSystem2D.instance.debugDrawFlags = EPhysics2DDrawFlags.Aabb |
// EPhysics2DDrawFlags.Pair |
// EPhysics2DDrawFlags.CenterOfMass |
// EPhysics2DDrawFlags.Joint |
// EPhysics2DDrawFlags.Shape;
Global.MAP_WIDTH = this.uiBg.contentSize.x;
Global.MAP_HIGHT = this.uiBg.contentSize.y;
......@@ -111,7 +114,7 @@ export class MainGame extends Scene {
wallRight.setPosition(Global.MAP_WIDTH / 2, 0);
wallRight.getComponent(UITransform).height = Global.MAP_HIGHT;
this.snake.init();
this.player.init();
// 初始化食物和NPC
this.fondManger.init(this.maxFood);
......@@ -123,86 +126,29 @@ export class MainGame extends Scene {
// 注册事件
director.on(Events.showGOver, this.showGOver, this);
director.on(Events.setGameState, this.setGameState, this);
// 仅在调试模式下添加键盘控制
if (this.DEBUG) {
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
}
}
onDestroy() {
MainGame._ins = null;
// 仅在调试模式下移除键盘事件监听
if (this.DEBUG) {
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
}
}
keyArr: number[] = [];
onKeyDown(event: EventKeyboard) {
const keyArr = [KeyCode.KEY_W, KeyCode.KEY_S, KeyCode.KEY_A, KeyCode.KEY_D,];
if (keyArr.indexOf(event.keyCode) > -1) {
this.keyArr.push(event.keyCode);
}
if (event.keyCode == KeyCode.SPACE) {
this.fastBtn.isFast = true;
}
this.setDir();
}
onKeyUp(event: EventKeyboard) {
const index = this.keyArr.indexOf(event.keyCode);
if (index > -1) {
this.keyArr.splice(index, 1);
}
if (event.keyCode == KeyCode.SPACE) {
this.fastBtn.isFast = false;
}
this.setDir();
}
setDir() {
if (!this.keyArr.length) return;
this.joystick.angle = {
[KeyCode.KEY_W]: 270,
[KeyCode.KEY_S]: 90,
[KeyCode.KEY_A]: 180,
[KeyCode.KEY_D]: 0,
}[this.keyArr[this.keyArr.length - 1]];
}
update(dt: number) {
if (this.state == GameState.READY) return;
// 更新UI提示
this.LTips.string = `长度:${this.snake.getSnakeLen()}}`;
this.LTips.string = `长度:${this.player.getSnakeLen()}}`;
this.snake.setAngle((360 - this.joystick.angle) % 360);
this.player.onUpdate(dt);
const speedFlagArr = [];
if (this.fastBtn.isFast) speedFlagArr.push(1);
const speedPlus = speedFlagArr.reduce((sum, item) => sum + item, 1);
this.snake.onUpdate(dt * speedPlus);
// 更新相机位置
this.camera.node.setPosition(this.player.head.getPosition());
// 更新NPC移动
this.animalNode.children.forEach(child => {
child.getComponent(Animal)?.autoMove(dt);
child.getComponent(AISnake)?.onUpdate(dt);
});
}
onPause() {
this.setGameState(GameState.PAUSE);
console.log("pause");
......@@ -226,7 +172,7 @@ export class MainGame extends Scene {
break;
case GameState.WIN:
director.pause();
console.log("win", this.snake.getSnakeLen());
console.log("win", this.player.getSnakeLen());
break;
case GameState.QUIT:
director.resume();
......@@ -237,38 +183,20 @@ export class MainGame extends Scene {
}
}
play() {
if (this.state == GameState.OVER) {
showToast("你已复活!");
this.state = GameState.PLAY;
this.snake.reInit(
math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50),
math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50)
);
// this.showGOver();
}
}
showGOver() {
console.log("showGOver", this.snake.getSnakeLen());
console.log("showGOver", this.player.getSnakeLen());
}
initItem = (index: number) => {
const node = PoolManager.instance.getNode(this.animalPrefab);
const x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
const y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
// const skinId = math.randomRangeInt(0, Config.SKIN_STYLE.length);
const skinId = 0;
node.getComponent(Animal)?.init(
node.getComponent(AISnake)?.init({
x, y,
math.randomRangeInt(0, 360),
skinId,
0.2,
Config.SKIN_STYLE[skinId].len,
Config.SKIN_STYLE[skinId].taitLen,
index + 1
);
angle: math.randomRangeInt(0, 360),
skinName: "default",
});
this.animalNode.addChild(node);
};
......
import {
_decorator, EventKeyboard, Input, input, KeyCode, director,
} from "cc";
import { Snake } from "./Snake";
import { Joystick } from "./Joystick";
import { FastBtn } from "./FastBtn";
import { Events, GameState } from "./Enums";
const { ccclass, property } = _decorator;
@ccclass("Player")
export class Player extends Snake {
@property(Joystick)
joystick: Joystick = null;
@property(FastBtn)
fastBtn: FastBtn = null;
onLoad() {
super.onLoad();
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
}
onDestroy() {
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
}
death() {
super.death();
// 发送游戏结束事件
director.emit(Events.setGameState, GameState.OVER);
}
keyArr: number[] = [];
onKeyDown(event: EventKeyboard) {
const keyArr = [KeyCode.KEY_W, KeyCode.KEY_S, KeyCode.KEY_A, KeyCode.KEY_D,];
if (keyArr.indexOf(event.keyCode) > -1) {
this.keyArr.push(event.keyCode);
}
if (event.keyCode == KeyCode.SPACE) {
this.fastBtn.isFast = true;
}
this.setDir();
}
onKeyUp(event: EventKeyboard) {
const index = this.keyArr.indexOf(event.keyCode);
if (index > -1) {
this.keyArr.splice(index, 1);
}
if (event.keyCode == KeyCode.SPACE) {
this.fastBtn.isFast = false;
}
this.setDir();
}
setDir() {
if (!this.keyArr.length) return;
this.joystick.angle = {
[KeyCode.KEY_W]: 270,
[KeyCode.KEY_S]: 90,
[KeyCode.KEY_A]: 180,
[KeyCode.KEY_D]: 0,
}[this.keyArr[this.keyArr.length - 1]];
}
onUpdate(dt: number) {
this.setAngle((360 - this.joystick.angle) % 360);
this.isFast = this.fastBtn.isFast;
super.onUpdate(dt);
}
}
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "3d36421e-8449-4d5b-86ba-bd43d0e06d42",
"files": [],
"subMetas": {},
"userData": {}
}
import {
_decorator,
assetManager,
Collider2D,
Component,
Contact2DType,
director, ImageAsset,
math,
Node,
PhysicsGroup,
Prefab,
Sprite,
SpriteAtlas,
SpriteFrame, TextAsset,
SpriteFrame,
tween,
v2,
v3,
Vec3,
} from "cc";
import { Events, FoodType, GameState } from "./Enums";
import { FoodType } from "./Enums";
import { Global } from "./Global";
import { PoolManager } from "./PoolManager";
import { isIntersect, loadSkin } from "./uitl";
import { MainGame } from "./MainGame";
import { executePreFrame, getItemGenerator } from "../../Utils/ExecutePreFrame";
import { BuffManager } from "./Buff/BuffManager";
import { BuffType } from "./Buff/BuffType";
const { ccclass, property } = _decorator;
......@@ -51,9 +52,9 @@ export class Snake extends Component {
private imgBody2: SpriteFrame = null;
// 蛇的状态
private isLife: boolean = false;
protected isLife: boolean = false;
private scale: number = 0.2;
private speed: number = 600;
protected speed: number = 600;
private energy: number = 0;
private tag: number = 0;
......@@ -66,10 +67,8 @@ export class Snake extends Component {
public async init(config: IInitConfig = {}) {
const {
x = 0, y = 0, angle = 0,
skinName = "default",
scale = 0.2,
bodyCount = 5,
x = 0, y = 0, angle = 0, scale = 0.2,
skinName = "default", bodyCount = 5,
} = config;
await this.setSkin(skinName);
......@@ -118,13 +117,17 @@ export class Snake extends Component {
onEnable() {
const collider = this.head.getComponent(Collider2D);
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
const eye = this.head.getChildByName("范围").getComponent(Collider2D);
eye.on(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
}
onDisable() {
const collider = this.head.getComponent(Collider2D);
collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
const eye = this.head.getChildByName("范围").getComponent(Collider2D);
eye.off(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
}
// 碰撞检测
......@@ -150,11 +153,6 @@ export class Snake extends Component {
if (foodType == FoodType.FOOD) {
this.addEnergy(1);
}
// 生成新的食物
if (MainGame.ins.fondManger.getFoodSum() < MainGame.ins.maxFood) {
MainGame.ins.fondManger.addFood();
}
})
.start();
}
......@@ -197,11 +195,11 @@ export class Snake extends Component {
this.bodyArr.splice(len, 0, newBody);
}
setAngle(e) {
this.isLife && (this.head.angle = e);
setAngle(angle: number) {
this.isLife && (this.head.angle = angle);
}
isFast = false;
private positions: Vec3[] = []; // 存储历史位置点
private readonly HISTORY_LENGTH = 100; // 增加历史点数量
private readonly SEGMENT_SPACING = 5; // 增加节点间距
......@@ -210,14 +208,22 @@ export class Snake extends Component {
totalTime = 0;
onUpdate(dt: number) {
this.totalTime += dt;
this.buffManager.update(dt);
let speedScale = 1;
if (this.isFast) {
speedScale += 1;
}
this.totalTime += dt * speedScale;
while (this.totalTime >= this.moveTime) {
this.totalTime -= this.moveTime;
this.move(this.moveTime);
}
}
move(dt: number) {
protected move(dt: number) {
if (!this.ready || !this.isLife) {
return;
}
......@@ -282,47 +288,75 @@ export class Snake extends Component {
this.death();
}
// 更新相机位置
MainGame.ins.camera.node.setPosition(this.head.getPosition());
}
getSnakeLen() {
return this.bodyArr.length;
}
reInit(e, t) {
this.head.angle = 0;
this.head.setPosition(e, t);
this.node.active = true;
this.isLife = true;
}
// 死亡处理
public death() {
if (!this.isLife) return;
// 隐藏所有身体节点
for (let i = 0; i < this.bodyArr.length; i++) {
this.bodyArr[i].setPosition(-9999, -9999);
this.bodyArr[i].active = false;
}
this.isLife = false;
this.node.active = false;
// 发送游戏结束事件
director.emit(Events.setGameState, GameState.OVER);
this.initFond(this.bodyArr.length);
}
// 工具方法 - 计算新位置
private getNewPos(angle: number, dt: number, currentPos: Vec3, speed: number = this.speed): Vec3 {
protected getNewPos(angle: number, dt: number, currentPos: Vec3, speed: number = this.speed): Vec3 {
const radian = angle / 180 * Math.PI;
const direction = v2(Math.cos(radian), Math.sin(radian));
return v3(
currentPos.x + dt * direction.x * speed,
currentPos.y + dt * direction.y * speed,
0
return v3(currentPos.x + dt * direction.x * speed, currentPos.y + dt * direction.y * speed, 0);
}
/**
* 初始化食物
*/
initItem = (index: number) => {
const bp = this.bodyArr[index].getPosition();
MainGame.ins.fondManger.addFood(
math.randomRangeInt(bp.x - 10, bp.x + 11),
math.randomRangeInt(bp.y - 20, bp.y + 21)
);
this.bodyArr[index].setPosition(9999, 9999);
this.bodyArr[index].active = false;
};
/**
* 初始化食物
*/
async initFond(count: number) {
await executePreFrame(getItemGenerator(count, this.initItem), 1, this);
this.ready = true;
}
private buffManager: BuffManager = new BuffManager(this);
private isInvincible: boolean = false;
private speedMultiplier: number = 1;
onLoad() {
// ... 其他初始化代码 ...
this.buffManager = new BuffManager(this);
}
// 添加Buff的便捷方法
addBuff(type: BuffType, duration?: number, value?: number) {
const buff = this.buffManager.createBuff(type, duration, value);
if (buff) {
this.buffManager.addBuff(buff);
}
}
// 提供给Buff使用的方法
setInvincible(value: boolean) {
this.isInvincible = value;
}
setSpeedMultiplier(value: number) {
this.speedMultiplier = value;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment