This commit is contained in:
sli97
2022-12-08 21:14:02 +08:00
parent 29104d4bed
commit 81a9a5a2f8
51 changed files with 1694 additions and 2815 deletions

View File

@@ -1,11 +0,0 @@
{
"extends": "@antfu",
"rules": {
"curly": "off",
"no-console": "off",
"no-cond-assign": "off",
"no-useless-call": "off",
"@typescript-eslint/brace-style": "off",
"@typescript-eslint/consistent-type-imports": "off"
}
}

View File

@@ -1887,7 +1887,7 @@
},
"component": "",
"_componentId": "a14b40zxfhFXKk12/1/OrYr",
"handler": "createRoom",
"handler": "handleCreateRoom",
"customEventData": ""
},
{

View File

@@ -1255,7 +1255,7 @@
}
],
"_interactable": true,
"_transition": 0,
"_transition": 3,
"_normalColor": {
"__type__": "cc.Color",
"r": 214,
@@ -1301,7 +1301,7 @@
"__expectedType__": "cc.SpriteFrame"
},
"_duration": 0.1,
"_zoomScale": 1.2,
"_zoomScale": 0.9,
"_target": {
"__id__": 30
},
@@ -1549,7 +1549,7 @@
}
],
"_interactable": true,
"_transition": 0,
"_transition": 3,
"_normalColor": {
"__type__": "cc.Color",
"r": 214,
@@ -1595,7 +1595,7 @@
"__expectedType__": "cc.SpriteFrame"
},
"_duration": 0.1,
"_zoomScale": 1.2,
"_zoomScale": 0.9,
"_target": {
"__id__": 38
},

View File

@@ -1,22 +1,21 @@
import { _decorator, Component } from 'cc';
import { EntityStateEnum } from '../Enum';
import StateMachine from './StateMachine';
import { _decorator, Component } from "cc";
import { EntityStateEnum } from "../Enum";
import StateMachine from "./StateMachine";
const { ccclass, property } = _decorator;
@ccclass('EntityManager')
@ccclass("EntityManager")
export abstract class EntityManager extends Component {
fsm: StateMachine
private _state: EntityStateEnum
fsm: StateMachine;
private _state: EntityStateEnum;
get state() {
return this._state
return this._state;
}
set state(newState) {
this._state = newState
this.fsm.setParams(newState, true)
this._state = newState;
this.fsm.setParams(newState, true);
}
abstract init(...args: any[]): void
abstract init(...args: any[]): void;
}

View File

@@ -1,13 +1,12 @@
export default class Singleton {
private static _instance: any = null
private static _instance: any = null;
static GetInstance<T>(): T {
if (this._instance === null) {
this._instance = new this()
this._instance = new this();
}
return this._instance
return this._instance;
}
protected constructor() {
}
}
protected constructor() {}
}

View File

@@ -1,48 +1,45 @@
import { animation, AnimationClip, Sprite, SpriteFrame } from 'cc'
import DataManager from '../Global/DataManager'
import { ResourceManager } from '../Global/ResourceManager'
import { sortSpriteFrame } from '../Utils'
import StateMachine from './StateMachine'
import { animation, AnimationClip, Sprite, SpriteFrame } from "cc";
import DataManager from "../Global/DataManager";
import { ResourceManager } from "../Global/ResourceManager";
import { sortSpriteFrame } from "../Utils";
import StateMachine from "./StateMachine";
/***
* unit:milisecond
*/
export const ANIMATION_SPEED = 1 / 10
export const ANIMATION_SPEED = 1 / 10;
/***
* 状态每组动画的承载容器持有SpriteAnimation组件执行播放
*/
export default class State {
private animationClip: AnimationClip
private animationClip: AnimationClip;
constructor(
private fsm: StateMachine,
private path: string,
private wrapMode: AnimationClip.WrapMode = AnimationClip.WrapMode.Normal,
private force: boolean = false,
private force: boolean = false
) {
//生成动画轨道属性
const track = new animation.ObjectTrack()
track.path = new animation.TrackPath().toComponent(Sprite).toProperty('spriteFrame')
const spriteFrames = DataManager.Instance.textureMap.get(this.path)
const frames: Array<[number, SpriteFrame]> = sortSpriteFrame(spriteFrames).map((item, index) => [
index * ANIMATION_SPEED,
item,
])
track.channel.curve.assignSorted(frames)
const track = new animation.ObjectTrack();
track.path = new animation.TrackPath().toComponent(Sprite).toProperty("spriteFrame");
const spriteFrames = DataManager.Instance.textureMap.get(this.path);
const frames: Array<[number, SpriteFrame]> = sortSpriteFrame(spriteFrames).map((item, index) => [index * ANIMATION_SPEED, item]);
track.channel.curve.assignSorted(frames);
//动画添加轨道
this.animationClip = new AnimationClip()
this.animationClip.name = this.path
this.animationClip.duration = frames.length * ANIMATION_SPEED
this.animationClip.addTrack(track)
this.animationClip.wrapMode = this.wrapMode
this.animationClip = new AnimationClip();
this.animationClip.name = this.path;
this.animationClip.duration = frames.length * ANIMATION_SPEED;
this.animationClip.addTrack(track);
this.animationClip.wrapMode = this.wrapMode;
}
run() {
if (this.fsm.animationComponent.defaultClip?.name === this.animationClip.name && !this.force) {
return
return;
}
this.fsm.animationComponent.defaultClip = this.animationClip
this.fsm.animationComponent.play()
this.fsm.animationComponent.defaultClip = this.animationClip;
this.fsm.animationComponent.play();
}
}

View File

@@ -1,30 +1,30 @@
import { _decorator, Animation, Component } from 'cc'
import { EntityTypeEnum } from '../Common'
import { FsmParamTypeEnum } from '../Enum'
const { ccclass } = _decorator
import State from './State'
import SubStateMachine from './SubStateMachine'
import { _decorator, Animation, Component } from "cc";
import { EntityTypeEnum } from "../Common";
import { FsmParamTypeEnum } from "../Enum";
const { ccclass } = _decorator;
import State from "./State";
import SubStateMachine from "./SubStateMachine";
type ParamsValueType = boolean | number
type ParamsValueType = boolean | number;
export interface IParamsValue {
type: FsmParamTypeEnum
value: ParamsValueType
type: FsmParamTypeEnum;
value: ParamsValueType;
}
export const getInitParamsTrigger = () => {
return {
type: FsmParamTypeEnum.Trigger,
value: false,
}
}
};
};
export const getInitParamsNumber = () => {
return {
type: FsmParamTypeEnum.Number,
value: 0,
}
}
};
};
/***
* 流动图
@@ -39,47 +39,47 @@ export const getInitParamsNumber = () => {
/***
* 有限状态机基类
*/
@ccclass('StateMachine')
@ccclass("StateMachine")
export default abstract class StateMachine extends Component {
private _currentState: State | SubStateMachine = null
params: Map<string, IParamsValue> = new Map()
stateMachines: Map<string, SubStateMachine | State> = new Map()
animationComponent: Animation
type: EntityTypeEnum
private _currentState: State | SubStateMachine = null;
params: Map<string, IParamsValue> = new Map();
stateMachines: Map<string, SubStateMachine | State> = new Map();
animationComponent: Animation;
type: EntityTypeEnum;
getParams(paramName: string) {
if (this.params.has(paramName)) {
return this.params.get(paramName).value
return this.params.get(paramName).value;
}
}
setParams(paramName: string, value: ParamsValueType) {
if (this.params.has(paramName)) {
this.params.get(paramName).value = value
this.run()
this.resetTrigger()
this.params.get(paramName).value = value;
this.run();
this.resetTrigger();
}
}
get currentState() {
return this._currentState
return this._currentState;
}
set currentState(newState) {
if (!newState) {
return
return;
}
this._currentState = newState
this._currentState.run()
this._currentState = newState;
this._currentState.run();
}
/***
* 清空所有trigger
*/
* 清空所有trigger
*/
resetTrigger() {
for (const [, value] of this.params) {
if (value.type === FsmParamTypeEnum.Trigger) {
value.value = false
value.value = false;
}
}
}
@@ -87,6 +87,6 @@ export default abstract class StateMachine extends Component {
/***
* 由子类重写方法目标是根据当前状态和参数修改currentState
*/
abstract init(...args: any[]): void
abstract run(): void
abstract init(...args: any[]): void;
abstract run(): void;
}

View File

@@ -1,30 +1,30 @@
import State from './State'
import StateMachine from './StateMachine'
import State from "./State";
import StateMachine from "./StateMachine";
/***
* 子有限状态机基类
* 用处例如有个idle的state但是有多个方向为了让主状态机更整洁可以把同类型的但具体不同的state都封装在子状态机中
*/
export default abstract class SubStateMachine {
private _currentState: State = null
stateMachines: Map<string, State> = new Map()
private _currentState: State = null;
stateMachines: Map<string, State> = new Map();
constructor(public fsm: StateMachine) {}
get currentState() {
return this._currentState
return this._currentState;
}
set currentState(newState) {
if (!newState) {
return
return;
}
this._currentState = newState
this._currentState.run()
this._currentState = newState;
this._currentState.run();
}
/***
* 具体类实现
*/
abstract run(): void
abstract run(): void;
}

View File

@@ -1,147 +1,141 @@
import { _decorator, instantiate, ProgressBar, Label, Vec3, Tween, tween, director } from 'cc';
import { EntityManager } from '../../Base/EntityManager';
import { ApiMsgEnum, EntityTypeEnum, IActor, InputTypeEnum, IVec2, toFixed } from '../../Common';
import { EntityStateEnum, EventEnum, SceneEnum } from '../../Enum';
import DataManager from '../../Global/DataManager';
import EventManager from '../../Global/EventManager';
import NetworkManager from '../../Global/NetworkManager';
import { rad2Angle } from '../../Utils';
import { WeaponManager } from '../Weapon/WeaponManager';
import { PlayerStateMachine } from './ActorStateMachine';
import { _decorator, instantiate, ProgressBar, Label, Vec3, Tween, tween } from "cc";
import { EntityManager } from "../../Base/EntityManager";
import { EntityTypeEnum, IActor, InputTypeEnum, IVec2, toFixed } from "../../Common";
import { EntityStateEnum, EventEnum, SceneEnum } from "../../Enum";
import DataManager from "../../Global/DataManager";
import EventManager from "../../Global/EventManager";
import { rad2Angle } from "../../Utils";
import { WeaponManager } from "../Weapon/WeaponManager";
import { ActorStateMachine } from "./ActorStateMachine";
const { ccclass } = _decorator;
@ccclass('ActorManager')
@ccclass("ActorManager")
export class ActorManager extends EntityManager implements IActor {
//静态数据
id: number
nickname: string
type: EntityTypeEnum
weaponType: EntityTypeEnum
bulletType: EntityTypeEnum
//静态数据
id: number;
nickname: string;
type: EntityTypeEnum;
weaponType: EntityTypeEnum;
bulletType: EntityTypeEnum;
//动态数据
hp: number
position: IVec2
direction: IVec2
//动态数据
hp: number;
position: IVec2;
direction: IVec2;
private hpBar: ProgressBar
private label: Label
private weapon: WeaponManager
private hpBar: ProgressBar;
private label: Label;
private weapon: WeaponManager;
private tw: Tween<any>
private targetPos: Vec3
private tw: Tween<unknown>;
private targetPos: Vec3;
private isDead = false;
private isDead = false
init(data: IActor) {
const { id, nickname, type, weaponType, bulletType } = data;
this.id = id;
this.nickname = nickname;
this.type = type;
this.weaponType = weaponType;
this.bulletType = bulletType;
get isSelf() {
return DataManager.Instance.myPlayerId === this.id
this.hpBar = this.node.getComponentInChildren(ProgressBar);
this.label = this.node.getComponentInChildren(Label);
this.label.string = nickname;
this.fsm = this.addComponent(ActorStateMachine);
this.fsm.init(type);
this.state = EntityStateEnum.Idle;
const weaponPrefab = DataManager.Instance.prefabMap.get(this.weaponType);
const weapon = instantiate(weaponPrefab);
weapon.setParent(this.node);
this.weapon = weapon.addComponent(WeaponManager);
this.weapon.init(data);
this.targetPos = undefined;
this.node.active = false;
}
async tick(dt: number) {
if (DataManager.Instance.myPlayerId !== this.id || this.isDead) {
return;
}
init(data: IActor) {
const { id, nickname, type, weaponType, bulletType } = data
this.id = id
this.nickname = nickname
this.type = type
this.weaponType = weaponType
this.bulletType = bulletType
this.hpBar = this.node.getComponentInChildren(ProgressBar)
this.label = this.node.getComponentInChildren(Label)
this.label.string = nickname
this.fsm = this.addComponent(PlayerStateMachine)
this.fsm.init(type)
this.state = EntityStateEnum.Idle
const weaponPrefab = DataManager.Instance.prefabMap.get(this.weaponType)
const weapon = instantiate(weaponPrefab)
weapon.setParent(this.node)
this.weapon = weapon.addComponent(WeaponManager)
this.weapon.init(data)
this.targetPos = undefined
this.node.active = false
if (this.hp <= 0) {
EventManager.Instance.emit(EventEnum.GameEnd);
this.isDead = true;
return;
}
async tick(dt: number) {
if (!this.isSelf) {
return
}
if (this.hp <= 0 && !this.isDead) {
EventManager.Instance.emit(EventEnum.GameEnd)
this.isDead = true
return
}
if (DataManager.Instance.jm.input.length()) {
const { x, y } = DataManager.Instance.jm.input
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
type: InputTypeEnum.ActorMove,
id: this.id,
direction: {
x: toFixed(x),
y: toFixed(y),
},
dt: toFixed(dt)
})
}
if (DataManager.Instance.jm.input.length()) {
const { x, y } = DataManager.Instance.jm.input;
EventManager.Instance.emit(EventEnum.ClientSync, {
type: InputTypeEnum.ActorMove,
id: this.id,
direction: {
x: toFixed(x),
y: toFixed(y),
},
dt: toFixed(dt),
});
}
}
render(data: IActor) {
this.renderHP(data)
this.renderPosition(data)
this.renderDirection(data)
render(data: IActor) {
this.renderHP(data);
this.renderPosition(data);
this.renderDirection(data);
}
renderHP(data: IActor) {
this.hp = data.hp;
this.hpBar.progress = data.hp / this.hpBar.totalLength;
}
renderPosition(data: IActor) {
const newPos = new Vec3(data.position.x, data.position.y);
if (!this.targetPos) {
this.node.active = true;
this.node.setPosition(newPos);
this.targetPos = new Vec3(newPos);
} else if (!this.targetPos.equals(newPos)) {
this.tw?.stop();
this.node.setPosition(this.targetPos);
this.targetPos.set(newPos);
this.state = EntityStateEnum.Run;
this.tw = tween(this.node)
.to(0.1, {
position: this.targetPos,
})
.call(() => {
this.state = EntityStateEnum.Idle;
})
.start();
}
// this.node.setPosition(data.position.x, data.position.y)
}
renderHP(data: IActor) {
this.hp = data.hp
this.hpBar.progress = data.hp / this.hpBar.totalLength
renderDirection(data: IActor) {
const { x, y } = data.direction;
if (x !== 0) {
this.node.setScale(x > 0 ? 1 : -1, 1);
this.label.node.setScale(x > 0 ? 1 : -1, 1);
this.hpBar.node.setScale(x > 0 ? 1 : -1, 1);
}
const side = Math.sqrt(x * x + y * y);
const angle = rad2Angle(Math.asin(y / side));
this.weapon.node.setRotationFromEuler(0, 0, angle);
renderPosition(data: IActor) {
const newPos = new Vec3(data.position.x, data.position.y)
if (!this.targetPos) {
this.node.active = true
this.node.setPosition(newPos)
this.targetPos = new Vec3(newPos)
} else if (!this.targetPos.equals(newPos)) {
this.tw?.stop()
this.node.setPosition(this.targetPos)
this.targetPos.set(newPos)
this.state = EntityStateEnum.Run
this.tw = tween(this.node).to(0.1, {
position: this.targetPos
}).call(() => {
this.state = EntityStateEnum.Idle
}).start()
}
// this.node.setPosition(data.position.x, data.position.y)
}
renderDirection(data: IActor) {
const { x, y } = data.direction
if (x !== 0) {
this.node.setScale(x > 0 ? 1 : -1, 1)
this.label.node.setScale(x > 0 ? 1 : -1, 1)
this.hpBar.node.setScale(x > 0 ? 1 : -1, 1)
}
const side = Math.sqrt(x * x + y * y)
const angle = rad2Angle(Math.asin(y / side))
this.weapon.node.setRotationFromEuler(0, 0, angle)
// const { x, y } = joystick.input
// let angle: number, sign: number
// if (x !== 0) {
// angle = rad2Angle(Math.atan(y / x))
// sign = joystick.input.x > 0 ? 1 : -1
// } else {
// angle = rad2Angle(Math.PI / 2)
// sign = joystick.input.y > 0 ? 1 : -1
// }
// this.node.setRotationFromEuler(0, 0, sign * angle)
}
// const { x, y } = joystick.input
// let angle: number, sign: number
// if (x !== 0) {
// angle = rad2Angle(Math.atan(y / x))
// sign = joystick.input.x > 0 ? 1 : -1
// } else {
// angle = rad2Angle(Math.PI / 2)
// sign = joystick.input.y > 0 ? 1 : -1
// }
// this.node.setRotationFromEuler(0, 0, sign * angle)
}
}

View File

@@ -1,48 +1,47 @@
import { _decorator, Animation, AnimationClip } from 'cc'
import State from '../../Base/State'
import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine'
import { EntityTypeEnum } from '../../Common'
import { EntityStateEnum, ParamsNameEnum } from '../../Enum'
const { ccclass } = _decorator
import { _decorator, Animation, AnimationClip } from "cc";
import State from "../../Base/State";
import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine";
import { EntityTypeEnum } from "../../Common";
import { EntityStateEnum, ParamsNameEnum } from "../../Enum";
const { ccclass } = _decorator;
@ccclass('PlayerStateMachine')
export class PlayerStateMachine extends StateMachine {
@ccclass("ActorStateMachine")
export class ActorStateMachine extends StateMachine {
init(type: EntityTypeEnum) {
this.type = type
this.animationComponent = this.node.addComponent(Animation)
this.initParams()
this.initStateMachines()
this.initAnimationEvent()
this.type = type;
this.animationComponent = this.node.addComponent(Animation);
this.initParams();
this.initStateMachines();
this.initAnimationEvent();
}
initParams() {
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger())
this.params.set(ParamsNameEnum.Run, getInitParamsTrigger())
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger());
this.params.set(ParamsNameEnum.Run, getInitParamsTrigger());
}
initStateMachines() {
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`, AnimationClip.WrapMode.Loop))
this.stateMachines.set(ParamsNameEnum.Run, new State(this, `${this.type}${EntityStateEnum.Run}`, AnimationClip.WrapMode.Loop))
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`, AnimationClip.WrapMode.Loop));
this.stateMachines.set(ParamsNameEnum.Run, new State(this, `${this.type}${EntityStateEnum.Run}`, AnimationClip.WrapMode.Loop));
}
initAnimationEvent() {
}
initAnimationEvent() {}
run() {
switch (this.currentState) {
case this.stateMachines.get(ParamsNameEnum.Idle):
case this.stateMachines.get(ParamsNameEnum.Run):
if (this.params.get(ParamsNameEnum.Run).value) {
this.currentState = this.stateMachines.get(ParamsNameEnum.Run)
this.currentState = this.stateMachines.get(ParamsNameEnum.Run);
} else if (this.params.get(ParamsNameEnum.Idle).value) {
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
} else {
this.currentState = this.currentState
this.currentState = this.currentState;
}
break
break;
default:
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
break
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
break;
}
}
}

View File

@@ -1,79 +1,83 @@
import { Tween, tween, Vec3, _decorator } from 'cc'
import { EntityManager } from '../../Base/EntityManager'
import { EntityTypeEnum, IBullet, IVec2 } from '../../Common'
import { EntityStateEnum, EventEnum } from '../../Enum'
import DataManager from '../../Global/DataManager'
import EventManager from '../../Global/EventManager'
import ObjectPoolManager from '../../Global/ObjectPoolManager'
import { rad2Angle } from '../../Utils'
import { ExplosionManager } from '../Explosion/ExplosionManager'
import { BulletStateMachine } from './BulletStateMachine'
const { ccclass } = _decorator
import { Tween, tween, Vec3, _decorator } from "cc";
import { EntityManager } from "../../Base/EntityManager";
import { EntityTypeEnum, IBullet, IVec2 } from "../../Common";
import { EntityStateEnum, EventEnum } from "../../Enum";
import DataManager from "../../Global/DataManager";
import EventManager from "../../Global/EventManager";
import ObjectPoolManager from "../../Global/ObjectPoolManager";
import { rad2Angle } from "../../Utils";
import { ExplosionManager } from "../Explosion/ExplosionManager";
import { BulletStateMachine } from "./BulletStateMachine";
const { ccclass } = _decorator;
@ccclass('BulletManager')
@ccclass("BulletManager")
export class BulletManager extends EntityManager implements IBullet {
//静态数据
id: number
owner: number
type: EntityTypeEnum
id: number;
owner: number;
type: EntityTypeEnum;
//动态数据
position: IVec2
direction: IVec2
position: IVec2;
direction: IVec2;
private angle: number
private tw: Tween<any>
private targetPos: Vec3
private angle: number;
private tw: Tween<any>;
private targetPos: Vec3;
init({ id, owner, type }: IBullet) {
this.id = id
this.owner = owner
this.type = type
this.id = id;
this.owner = owner;
this.type = type;
this.fsm = this.addComponent(BulletStateMachine)
this.fsm.init(type)
this.state = EntityStateEnum.Idle
this.node.active = false
this.targetPos = undefined
this.fsm = this.addComponent(BulletStateMachine);
this.fsm.init(type);
this.state = EntityStateEnum.Idle;
EventManager.Instance.on(EventEnum.ExplosionBorn, this.handleExplosion, this)
this.node.active = false;
this.targetPos = undefined;
this.angle = undefined;
EventManager.Instance.on(EventEnum.ExplosionBorn, this.handleExplosion, this);
}
handleExplosion(id: number, { x, y }: IVec2) {
if (this.id !== id) {
return
return;
}
const explosion = ObjectPoolManager.Instance.get(EntityTypeEnum.Explosion)
const explosionManager = explosion.getComponent(ExplosionManager) || explosion.addComponent(ExplosionManager)
const explosion = ObjectPoolManager.Instance.get(EntityTypeEnum.Explosion);
const explosionManager = explosion.getComponent(ExplosionManager) || explosion.addComponent(ExplosionManager);
explosionManager.init(EntityTypeEnum.Explosion, {
x, y,
})
x,
y,
});
EventManager.Instance.off(EventEnum.ExplosionBorn, this.handleExplosion, this)
ObjectPoolManager.Instance.ret(this.node)
DataManager.Instance.bulletMap.delete(this.id)
this.angle = undefined
EventManager.Instance.off(EventEnum.ExplosionBorn, this.handleExplosion, this);
ObjectPoolManager.Instance.ret(this.node);
DataManager.Instance.bulletMap.delete(this.id);
}
render(data: IBullet) {
this.renderPosition(data)
this.renderDirection(data)
this.renderPosition(data);
this.renderDirection(data);
}
renderPosition(data: IBullet) {
const newPos = new Vec3(data.position.x, data.position.y)
const newPos = new Vec3(data.position.x, data.position.y);
if (!this.targetPos) {
this.node.active = true
this.node.setPosition(newPos)
this.targetPos = new Vec3(newPos)
this.node.active = true;
this.node.setPosition(newPos);
this.targetPos = new Vec3(newPos);
} else if (!this.targetPos.equals(newPos)) {
this.tw?.stop()
this.node.setPosition(this.targetPos)
this.targetPos.set(newPos)
this.tw = tween(this.node).to(0.1, {
position: this.targetPos
}).start()
this.tw?.stop();
this.node.setPosition(this.targetPos);
this.targetPos.set(newPos);
this.tw = tween(this.node)
.to(0.1, {
position: this.targetPos,
})
.start();
}
// this.node.setPosition(data.position.x, data.position.y)
@@ -81,12 +85,12 @@ export class BulletManager extends EntityManager implements IBullet {
renderDirection(data: IBullet) {
if (this.angle === undefined) {
const { x, y } = data.direction
const side = Math.sqrt(x * x + y * y)
this.angle = x > 0 ? rad2Angle(Math.asin(y / side)) : rad2Angle(Math.asin(- y / side)) + 180
const { x, y } = data.direction;
const side = Math.sqrt(x * x + y * y);
this.angle = x > 0 ? rad2Angle(Math.asin(y / side)) : rad2Angle(Math.asin(-y / side)) + 180;
}
this.node.setRotationFromEuler(0, 0, this.angle)
this.node.setRotationFromEuler(0, 0, this.angle);
// let angle: number, sign: number
// if (x !== 0) {

View File

@@ -1,45 +1,43 @@
import { _decorator, Animation } from 'cc'
import State from '../../Base/State'
import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine'
import { EntityTypeEnum } from '../../Common'
import { EntityStateEnum, ParamsNameEnum } from '../../Enum'
const { ccclass } = _decorator
import { _decorator, Animation } from "cc";
import State from "../../Base/State";
import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine";
import { EntityTypeEnum } from "../../Common";
import { EntityStateEnum, ParamsNameEnum } from "../../Enum";
const { ccclass } = _decorator;
@ccclass('BulletStateMachine')
@ccclass("BulletStateMachine")
export class BulletStateMachine extends StateMachine {
init(type: EntityTypeEnum) {
this.type = type
this.animationComponent = this.node.addComponent(Animation)
this.type = type;
this.animationComponent = this.node.addComponent(Animation);
this.initParams()
this.initStateMachines()
this.initAnimationEvent()
this.initParams();
this.initStateMachines();
this.initAnimationEvent();
}
initParams() {
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger())
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger());
}
initStateMachines() {
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`))
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`));
}
initAnimationEvent() {
}
initAnimationEvent() {}
run() {
switch (this.currentState) {
case this.stateMachines.get(ParamsNameEnum.Idle):
if (this.params.get(ParamsNameEnum.Idle).value) {
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
} else {
this.currentState = this.currentState
this.currentState = this.currentState;
}
break
break;
default:
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
break
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
break;
}
}
}

View File

@@ -1,17 +1,16 @@
import { _decorator } from 'cc'
import { EntityManager } from '../../Base/EntityManager'
import { EntityTypeEnum, IVec2 } from '../../Common'
import { EntityStateEnum } from '../../Enum'
import { ExplosionStateMachine } from './ExplosionStateMachine'
const { ccclass, property } = _decorator
import { _decorator } from "cc";
import { EntityManager } from "../../Base/EntityManager";
import { EntityTypeEnum, IVec2 } from "../../Common";
import { EntityStateEnum } from "../../Enum";
import { ExplosionStateMachine } from "./ExplosionStateMachine";
const { ccclass, property } = _decorator;
@ccclass('ExplosionManager')
@ccclass("ExplosionManager")
export class ExplosionManager extends EntityManager {
init(type: EntityTypeEnum, { x, y }: IVec2) {
this.node.setPosition(x, y)
this.fsm = this.addComponent(ExplosionStateMachine)
this.fsm.init(type)
this.state = EntityStateEnum.Idle
this.node.setPosition(x, y);
this.fsm = this.addComponent(ExplosionStateMachine);
this.fsm.init(type);
this.state = EntityStateEnum.Idle;
}
}

View File

@@ -1,52 +1,52 @@
import { _decorator, Animation } from 'cc'
import State from '../../Base/State'
import StateMachine, { getInitParamsTrigger } from '../../Base/StateMachine'
import { EntityTypeEnum } from '../../Common'
import { EntityStateEnum, ParamsNameEnum } from '../../Enum'
import ObjectPoolManager from '../../Global/ObjectPoolManager'
const { ccclass, property } = _decorator
import { _decorator, Animation } from "cc";
import State from "../../Base/State";
import StateMachine, { getInitParamsTrigger } from "../../Base/StateMachine";
import { EntityTypeEnum } from "../../Common";
import { EntityStateEnum, ParamsNameEnum } from "../../Enum";
import ObjectPoolManager from "../../Global/ObjectPoolManager";
const { ccclass, property } = _decorator;
@ccclass('ExplosionStateMachine')
@ccclass("ExplosionStateMachine")
export class ExplosionStateMachine extends StateMachine {
init(type: EntityTypeEnum) {
this.type = type
this.animationComponent = this.node.addComponent(Animation)
this.type = type;
this.animationComponent = this.node.addComponent(Animation);
this.initParams()
this.initStateMachines()
this.initAnimationEvent()
this.initParams();
this.initStateMachines();
this.initAnimationEvent();
}
initParams() {
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger())
this.params.set(ParamsNameEnum.Idle, getInitParamsTrigger());
}
initStateMachines() {
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`))
this.stateMachines.set(ParamsNameEnum.Idle, new State(this, `${this.type}${EntityStateEnum.Idle}`));
}
initAnimationEvent() {
this.animationComponent.on(Animation.EventType.FINISHED, () => {
const whiteList = [EntityStateEnum.Idle]
const name = this.animationComponent.defaultClip.name
if (whiteList.some(v => name.includes(v))) {
ObjectPoolManager.Instance.ret(this.node)
const whiteList = [EntityStateEnum.Idle];
const name = this.animationComponent.defaultClip.name;
if (whiteList.some((v) => name.includes(v))) {
ObjectPoolManager.Instance.ret(this.node);
}
})
});
}
run() {
switch (this.currentState) {
case this.stateMachines.get(ParamsNameEnum.Idle):
if (this.params.get(ParamsNameEnum.Idle).value) {
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
} else {
this.currentState = this.currentState
this.currentState = this.currentState;
}
break
break;
default:
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle)
break
this.currentState = this.stateMachines.get(ParamsNameEnum.Idle);
break;
}
}
}

View File

@@ -1,66 +1,61 @@
import { _decorator, Node, Vec2, UITransform } from 'cc'
import { EntityManager } from '../../Base/EntityManager'
import { ApiMsgEnum, EntityTypeEnum, InputTypeEnum, toFixed } from '../../Common'
import { EntityStateEnum, EventEnum } from '../../Enum'
import DataManager from '../../Global/DataManager'
import EventManager from '../../Global/EventManager'
import NetworkManager from '../../Global/NetworkManager'
import { WeaponStateMachine } from './WeaponStateMachine'
const { ccclass } = _decorator
import { _decorator, Node, Vec2, UITransform } from "cc";
import { EntityManager } from "../../Base/EntityManager";
import { EntityTypeEnum, InputTypeEnum, toFixed } from "../../Common";
import { EntityStateEnum, EventEnum } from "../../Enum";
import DataManager from "../../Global/DataManager";
import EventManager from "../../Global/EventManager";
import { WeaponStateMachine } from "./WeaponStateMachine";
const { ccclass } = _decorator;
@ccclass('WeaponManager')
@ccclass("WeaponManager")
export class WeaponManager extends EntityManager {
owner: number
type: EntityTypeEnum
owner: number;
type: EntityTypeEnum;
private body: Node
private anchor: Node
private point: Node
private body: Node;
private anchor: Node;
private point: Node;
get isSelf() {
return DataManager.Instance.myPlayerId === this.owner
}
init({ id, weaponType }: { id: number; weaponType: EntityTypeEnum }) {
this.owner = id;
this.type = weaponType;
init({ id, weaponType }: { id: number, weaponType: EntityTypeEnum }) {
this.owner = id
this.type = weaponType
this.node.setSiblingIndex(0);
this.body = this.node.getChildByName("Body");
this.anchor = this.body.getChildByName("Anchor");
this.point = this.anchor.getChildByName("Point");
this.node.setSiblingIndex(0)
this.body = this.node.getChildByName("Body")
this.anchor = this.body.getChildByName("Anchor")
this.point = this.anchor.getChildByName("Point")
this.fsm = this.body.addComponent(WeaponStateMachine);
this.fsm.init(weaponType);
this.state = EntityStateEnum.Idle;
this.fsm = this.body.addComponent(WeaponStateMachine)
this.fsm.init(weaponType)
this.state = EntityStateEnum.Idle
EventManager.Instance.on(EventEnum.WeaponShoot, this.handleWeaponShoot, this)
EventManager.Instance.on(EventEnum.BulletBorn, this.handleBulletBorn, this)
EventManager.Instance.on(EventEnum.WeaponShoot, this.handleWeaponShoot, this);
EventManager.Instance.on(EventEnum.BulletBorn, this.handleBulletBorn, this);
}
onDestroy() {
EventManager.Instance.off(EventEnum.WeaponShoot, this.handleWeaponShoot, this)
EventManager.Instance.off(EventEnum.BulletBorn, this.handleBulletBorn, this)
EventManager.Instance.off(EventEnum.WeaponShoot, this.handleWeaponShoot, this);
EventManager.Instance.off(EventEnum.BulletBorn, this.handleBulletBorn, this);
}
handleBulletBorn(owner: number) {
if (this.owner !== owner) {
return
return;
}
this.state = EntityStateEnum.Attack
this.state = EntityStateEnum.Attack;
}
handleWeaponShoot() {
if (!this.isSelf) {
return
if (DataManager.Instance.myPlayerId !== this.owner) {
return;
}
const pointWorldPos = this.point.getWorldPosition()
const pointWorldPos = this.point.getWorldPosition();
const pointStagePos = DataManager.Instance.stage.getComponent(UITransform).convertToNodeSpaceAR(pointWorldPos);
const anchorWorldPos = this.anchor.getWorldPosition()
const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize()
const anchorWorldPos = this.anchor.getWorldPosition();
const directionVec2 = new Vec2(pointWorldPos.x - anchorWorldPos.x, pointWorldPos.y - anchorWorldPos.y).normalize();
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
EventManager.Instance.emit(EventEnum.ClientSync, {
type: InputTypeEnum.WeaponShoot,
owner: this.owner,
position: {
@@ -71,6 +66,6 @@ export class WeaponManager extends EntityManager {
x: toFixed(directionVec2.x),
y: toFixed(directionVec2.y),
},
})
});
}
}

View File

@@ -1,59 +1,60 @@
export enum FsmParamTypeEnum {
Number = 'Number',
Trigger = 'Trigger',
}
export enum EntityStateEnum {
Idle = 'Idle',
Run = 'Run',
Attack = 'Attack',
Number = "Number",
Trigger = "Trigger",
}
export enum ParamsNameEnum {
Idle = 'Idle',
Run = 'Run',
Attack = 'Attack',
Idle = "Idle",
Run = "Run",
Attack = "Attack",
}
export enum EntityStateEnum {
Idle = "Idle",
Run = "Run",
Attack = "Attack",
}
export enum EventEnum {
WeaponShoot = 'WeaponShoot',
BulletBorn = 'BulletBorn',
ExplosionBorn = 'ExplosionBorn',
RoomJoin = 'RoomJoin',
GameStart = 'GameStart',
GameEnd = 'GameEnd',
WeaponShoot = "WeaponShoot",
BulletBorn = "BulletBorn",
ExplosionBorn = "ExplosionBorn",
RoomJoin = "RoomJoin",
GameStart = "GameStart",
GameEnd = "GameEnd",
ClientSync = "ClientSync",
}
export enum PrefabPathEnum {
Map1 = 'prefab/Map1',
Actor1 = 'prefab/Actor',
Actor2 = 'prefab/Actor',
Weapon1 = 'prefab/Weapon1',
Weapon2 = 'prefab/Weapon2',
Bullet1 = 'prefab/Bullet',
Bullet2 = 'prefab/Bullet',
Explosion = 'prefab/Explosion',
JoyStick = 'prefab/JoyStick',
Shoot = 'prefab/Shoot',
Map1 = "prefab/Map1",
Actor1 = "prefab/Actor",
Actor2 = "prefab/Actor",
Weapon1 = "prefab/Weapon1",
Weapon2 = "prefab/Weapon2",
Bullet1 = "prefab/Bullet",
Bullet2 = "prefab/Bullet",
Explosion = "prefab/Explosion",
JoyStick = "prefab/JoyStick",
Shoot = "prefab/Shoot",
}
export enum TexturePathEnum {
Actor1Idle = 'texture/actor/actor1/idle',
Actor1Run = 'texture/actor/actor1/run',
Actor2Idle = 'texture/actor/actor2/idle',
Actor2Run = 'texture/actor/actor2/run',
Weapon1Idle = 'texture/weapon/weapon1/idle',
Weapon1Attack = 'texture/weapon/weapon1/attack',
Weapon2Idle = 'texture/weapon/weapon2/idle',
Weapon2Attack = 'texture/weapon/weapon2/attack',
Bullet1Idle = 'texture/bullet/bullet1',
Bullet2Idle = 'texture/bullet/bullet2',
ExplosionIdle = 'texture/explosion',
Actor1Idle = "texture/actor/actor1/idle",
Actor1Run = "texture/actor/actor1/run",
Actor2Idle = "texture/actor/actor2/idle",
Actor2Run = "texture/actor/actor2/run",
Weapon1Idle = "texture/weapon/weapon1/idle",
Weapon1Attack = "texture/weapon/weapon1/attack",
Weapon2Idle = "texture/weapon/weapon2/idle",
Weapon2Attack = "texture/weapon/weapon2/attack",
Bullet1Idle = "texture/bullet/bullet1",
Bullet2Idle = "texture/bullet/bullet2",
ExplosionIdle = "texture/explosion",
}
export enum SceneEnum {
Login = 'Login',
Hall = 'Hall',
Room = 'Room',
Battle = 'Battle',
}
Login = "Login",
Hall = "Hall",
Room = "Room",
Battle = "Battle",
}

View File

@@ -1,151 +1,160 @@
import { Node, Prefab, SpriteFrame, clamp } from 'cc'
import Singleton from '../Base/Singleton'
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from '../Common'
import { ActorManager } from '../Entity/Actor/ActorManager'
import { BulletManager } from '../Entity/Bullet/BulletManager'
import { EventEnum } from '../Enum'
import { JoyStickManager } from '../UI/JoyStickManager'
import EventManager from './EventManager'
import { Node, Prefab, SpriteFrame, clamp } from "cc";
import Singleton from "../Base/Singleton";
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from "../Common";
import { ActorManager } from "../Entity/Actor/ActorManager";
import { BulletManager } from "../Entity/Bullet/BulletManager";
import { EventEnum } from "../Enum";
import { JoyStickManager } from "../UI/JoyStickManager";
import EventManager from "./EventManager";
const PLAYER_SPEED = 100;
const BULLET_SPEED = 600;
const PLAYER_SPEED = 100
const BULLET_SPEED = 600
const WEAPON_DAMAGE = 5;
const WEAPON_DAMAGE = 5
const PLAYER_RADIUS = 50;
const BULLET_RADIUS = 10;
const PLAYER_RADIUS = 50
const BULLET_RADIUS = 10
const mapW = 960
const mapH = 640
const mapW = 960;
const mapH = 640;
export default class DataManager extends Singleton {
static get Instance() {
return super.GetInstance<DataManager>()
return super.GetInstance<DataManager>();
}
//登陆数据
myPlayerId = 1
myPlayerId = 1;
//大厅数据
roomInfo: IRoom
roomInfo: IRoom;
//游戏数据
stage: Node
jm: JoyStickManager
prefabMap: Map<string, Prefab> = new Map()
textureMap: Map<string, SpriteFrame[]> = new Map()
actorMap: Map<number, ActorManager> = new Map()
bulletMap: Map<number, BulletManager> = new Map()
stage: Node;
jm: JoyStickManager;
prefabMap: Map<string, Prefab> = new Map();
textureMap: Map<string, SpriteFrame[]> = new Map();
actorMap: Map<number, ActorManager> = new Map();
bulletMap: Map<number, BulletManager> = new Map();
reset() {
this.stage = null
this.jm = null
this.actorMap.clear()
this.bulletMap.clear()
this.prefabMap.clear()
this.textureMap.clear()
this.frameId = 0;
this.stage = null;
this.jm = null;
this.actorMap.clear();
this.bulletMap.clear();
this.prefabMap.clear();
this.textureMap.clear();
}
frameId = 0;
lastState: IState;
state: IState = {
players: [{
id: 2,
nickname: "哈哈1",
position: {
x: -200,
y: -200
actors: [
{
id: 2,
nickname: "哈哈1",
position: {
x: -200,
y: -200,
},
direction: {
x: 1,
y: 0,
},
hp: 100,
type: EntityTypeEnum.Actor1,
weaponType: EntityTypeEnum.Weapon1,
bulletType: EntityTypeEnum.Bullet1,
},
direction: {
x: 1,
y: 0
{
id: 1,
nickname: "哈哈2",
position: {
x: 200,
y: 200,
},
direction: {
x: 0,
y: -1,
},
hp: 100,
type: EntityTypeEnum.Actor2,
weaponType: EntityTypeEnum.Weapon2,
bulletType: EntityTypeEnum.Bullet2,
},
hp: 100,
type: EntityTypeEnum.Actor1,
weaponType: EntityTypeEnum.Weapon1,
bulletType: EntityTypeEnum.Bullet1,
}, {
id: 1,
nickname: "哈哈2",
position: {
x: 200,
y: 200
},
direction: {
x: 0,
y: -1
},
hp: 100,
type: EntityTypeEnum.Actor2,
weaponType: EntityTypeEnum.Weapon2,
bulletType: EntityTypeEnum.Bullet2,
}],
],
bullets: [],
nextBulletId: 1
}
nextBulletId: 1,
};
applyInput(input: IClientInput) {
switch (input.type) {
case InputTypeEnum.ActorMove: {
const { direction: { x, y }, dt, id } = input
const player = this.state.players.find(e => e.id === id)
const {
direction: { x, y },
dt,
id,
} = input;
const player = this.state.actors.find((e) => e.id === id);
if (!player) {
return
return;
}
player.position.x += toFixed(x * PLAYER_SPEED * dt)
player.position.y += toFixed(y * PLAYER_SPEED * dt)
player.position.x += toFixed(x * PLAYER_SPEED * dt);
player.position.y += toFixed(y * PLAYER_SPEED * dt);
player.position.x = clamp(player.position.x, -mapW / 2, mapW / 2)
player.position.y = clamp(player.position.y, -mapH / 2, mapH / 2)
player.position.x = clamp(player.position.x, -mapW / 2, mapW / 2);
player.position.y = clamp(player.position.y, -mapH / 2, mapH / 2);
player.direction = { x, y }
break
player.direction = { x, y };
break;
}
case InputTypeEnum.WeaponShoot: {
const { owner, position, direction } = input
const { owner, position, direction } = input;
const bullet: IBullet = {
id: this.state.nextBulletId++,
owner,
position,
direction,
type: this.actorMap.get(owner).bulletType
}
this.state.bullets.push(bullet)
type: this.actorMap.get(owner).bulletType,
};
this.state.bullets.push(bullet);
EventManager.Instance.emit(EventEnum.BulletBorn, owner)
break
EventManager.Instance.emit(EventEnum.BulletBorn, owner);
break;
}
case InputTypeEnum.TimePast: {
const { dt } = input
const { bullets, players } = this.state
const { dt } = input;
const { bullets, actors } = this.state;
for (let i = bullets.length - 1; i >= 0; i--) {
const bullet = bullets[i];
for (let j = players.length - 1; j >= 0; j--) {
const player = players[j];
if (((player.position.x - bullet.position.x) ** 2 + (player.position.y - bullet.position.y) ** 2) < (PLAYER_RADIUS + BULLET_RADIUS) ** 2) {
for (let j = actors.length - 1; j >= 0; j--) {
const player = actors[j];
if ((player.position.x - bullet.position.x) ** 2 + (player.position.y - bullet.position.y) ** 2 < (PLAYER_RADIUS + BULLET_RADIUS) ** 2) {
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
x: toFixed((player.position.x + bullet.position.x) / 2),
y: toFixed((player.position.y + bullet.position.y) / 2),
})
});
player.hp -= WEAPON_DAMAGE
bullets.splice(i, 1)
break
player.hp -= WEAPON_DAMAGE;
bullets.splice(i, 1);
break;
}
}
if (Math.abs(bullet.position.x) > mapW / 2 || Math.abs(bullet.position.y) > mapH / 2) {
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
x: bullet.position.x,
y: bullet.position.y,
})
bullets.splice(i, 1)
});
bullets.splice(i, 1);
}
}
for (const bullet of this.state.bullets) {
bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt)
bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt)
bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt);
bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt);
}
}
}

View File

@@ -1,5 +1,5 @@
import Singleton from '../Base/Singleton'
import { EventEnum } from '../Enum';
import Singleton from "../Base/Singleton";
import { EventEnum } from "../Enum";
interface IItem {
cb: Function;
@@ -23,7 +23,7 @@ export default class EventManager extends Singleton {
off(event: EventEnum, cb: Function, ctx: unknown) {
if (this.map.has(event)) {
const index = this.map.get(event).findIndex(i => cb === i.cb && i.ctx === ctx);
const index = this.map.get(event).findIndex((i) => cb === i.cb && i.ctx === ctx);
index > -1 && this.map.get(event).splice(index, 1);
}
}
@@ -31,7 +31,7 @@ export default class EventManager extends Singleton {
emit(event: EventEnum, ...params: unknown[]) {
if (this.map.has(event)) {
this.map.get(event).forEach(({ cb, ctx }) => {
cb.apply(ctx, params)
cb.apply(ctx, params);
});
}
}

View File

@@ -1,8 +1,8 @@
import Singleton from '../Base/Singleton'
import { ApiMsgEnum, IModel } from '../Common';
import { binaryEncode, binaryDecode } from '../Common/Binary';
import Singleton from "../Base/Singleton";
import { ApiMsgEnum, IModel } from "../Common";
import { binaryEncode, binaryDecode } from "../Common/Binary";
const TIMEOUT = 5000
const TIMEOUT = 5000;
interface IItem {
cb: Function;
@@ -12,109 +12,107 @@ interface IItem {
export interface ICallApiRet<T> {
success: boolean;
error?: Error;
res?: T
res?: T;
}
type aaa = keyof IModel
export default class NetworkManager extends Singleton {
static get Instance() {
return super.GetInstance<NetworkManager>()
return super.GetInstance<NetworkManager>();
}
ws: WebSocket
port = 8888
maps: Map<ApiMsgEnum, Array<IItem>> = new Map()
isConnected = false
ws: WebSocket;
port = 8888;
maps: Map<ApiMsgEnum, Array<IItem>> = new Map();
isConnected = false;
connect() {
return new Promise((resolve, reject) => {
if (this.isConnected) {
resolve(true)
return
resolve(true);
return;
}
this.ws = new WebSocket(`ws://localhost:${this.port}`)
this.ws = new WebSocket(`ws://localhost:${this.port}`);
//onmessage接受的数据类型只有在后端返回字节数组的时候才有效果
this.ws.binaryType = 'arraybuffer';
this.ws.binaryType = "arraybuffer";
this.ws.onopen = () => {
this.isConnected = true
resolve(true)
}
this.isConnected = true;
resolve(true);
};
this.ws.onerror = (e) => {
this.isConnected = false
console.log(e)
reject("ws onerror")
}
this.isConnected = false;
console.log(e);
reject("ws onerror");
};
this.ws.onclose = () => {
this.isConnected = false
reject("ws onclose")
}
this.isConnected = false;
reject("ws onclose");
};
this.ws.onmessage = (e) => {
try {
const json = binaryDecode(e.data)
const { name, data } = json
const json = binaryDecode(e.data);
const { name, data } = json;
try {
if (this.maps.has(name) && this.maps.get(name).length) {
console.log(json);
this.maps.get(name).forEach(({ cb, ctx }) => cb.call(ctx, data))
this.maps.get(name).forEach(({ cb, ctx }) => cb.call(ctx, data));
}
} catch (error) {
console.log("onmessage:", error)
console.log("onmessage:", error);
}
} catch (error) {
console.log('解析失败不是合法的JSON格式', error)
console.log("解析失败不是合法的JSON格式", error);
}
}
})
};
});
}
callApi<T extends keyof IModel['api']>(name: T, data: IModel['api'][T]['req']): Promise<ICallApiRet<IModel['api'][T]['res']>> {
callApi<T extends keyof IModel["api"]>(name: T, data: IModel["api"][T]["req"]): Promise<ICallApiRet<IModel["api"][T]["res"]>> {
return new Promise((resolve) => {
try {
// 超时处理
const timer = setTimeout(() => {
resolve({ success: false, error: new Error('timeout') })
this.unlistenMsg(name as any, cb, null)
}, TIMEOUT)
resolve({ success: false, error: new Error("timeout") });
this.unlistenMsg(name as any, cb, null);
}, TIMEOUT);
// 回调处理
const cb = (res) => {
resolve(res)
clearTimeout(timer)
this.unlistenMsg(name as any, cb, null)
}
this.listenMsg(name as any, cb, null)
resolve(res);
clearTimeout(timer);
this.unlistenMsg(name as any, cb, null);
};
this.listenMsg(name as any, cb, null);
this.sendMsg(name as any, data)
this.sendMsg(name as any, data);
} catch (error) {
resolve({ success: false, error: error as Error })
resolve({ success: false, error: error as Error });
}
})
});
}
async sendMsg<T extends keyof IModel['msg']>(name: T, data: IModel['msg'][T]) {
const view = binaryEncode(name, data)
let delay = parseInt(new URLSearchParams(location.search).get('delay') || '0') || 0;
await new Promise((r) => setTimeout(r, delay))
this.ws.send(view.buffer)
async sendMsg<T extends keyof IModel["msg"]>(name: T, data: IModel["msg"][T]) {
const view = binaryEncode(name, data);
let delay = parseInt(new URLSearchParams(location.search).get("delay") || "0") || 0;
await new Promise((r) => setTimeout(r, delay));
this.ws.send(view.buffer);
}
listenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) {
listenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
if (this.maps.has(name)) {
this.maps.get(name).push({ ctx, cb })
this.maps.get(name).push({ ctx, cb });
} else {
this.maps.set(name, [{ ctx, cb }])
this.maps.set(name, [{ ctx, cb }]);
}
}
unlistenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) {
unlistenMsg<T extends keyof IModel["msg"]>(name: T, cb: (args: IModel["msg"][T]) => void, ctx: unknown) {
if (this.maps.has(name)) {
const items = this.maps.get(name)
const index = items.findIndex(i => cb === i.cb && i.ctx === ctx);
index > -1 && items.splice(index, 1)
const items = this.maps.get(name);
const index = items.findIndex((i) => cb === i.cb && i.ctx === ctx);
index > -1 && items.splice(index, 1);
}
}
}

View File

@@ -1,55 +1,55 @@
import Singleton from '../Base/Singleton'
import { instantiate, Node } from 'cc'
import DataManager from './DataManager'
import { EntityTypeEnum } from '../Common'
import Singleton from "../Base/Singleton";
import { instantiate, Node } from "cc";
import DataManager from "./DataManager";
import { EntityTypeEnum } from "../Common";
export default class ObjectPoolManager extends Singleton {
static get Instance() {
return super.GetInstance<ObjectPoolManager>()
return super.GetInstance<ObjectPoolManager>();
}
private objectPool: Node = null
private map: Map<EntityTypeEnum, Node[]> = new Map()
private objectPool: Node = null;
private map: Map<EntityTypeEnum, Node[]> = new Map();
private getContainerName(objectName: EntityTypeEnum) {
return objectName + 'Pool'
return objectName + "Pool";
}
reset() {
this.objectPool = null
this.map.clear()
this.objectPool = null;
this.map.clear();
}
get(objectName: EntityTypeEnum) {
if (this.objectPool === null) {
this.objectPool = new Node("ObjectPool")
this.objectPool.setParent(DataManager.Instance.stage)
this.objectPool = new Node("ObjectPool");
this.objectPool.setParent(DataManager.Instance.stage);
}
if (!this.map.has(objectName)) {
this.map.set(objectName, [])
const container = new Node(this.getContainerName(objectName))
container.setParent(this.objectPool)
this.map.set(objectName, []);
const container = new Node(this.getContainerName(objectName));
container.setParent(this.objectPool);
}
let node: Node
const nodes = this.map.get(objectName)
let node: Node;
const nodes = this.map.get(objectName);
if (!nodes.length) {
const prefab = DataManager.Instance.prefabMap.get(objectName)
node = instantiate(prefab)
node.name = objectName
node.setParent(this.objectPool.getChildByName(this.getContainerName(objectName)))
const prefab = DataManager.Instance.prefabMap.get(objectName);
node = instantiate(prefab);
node.name = objectName;
node.setParent(this.objectPool.getChildByName(this.getContainerName(objectName)));
} else {
node = nodes.pop()
node = nodes.pop();
}
node.active = true
return node
node.active = true;
return node;
}
ret(object: Node) {
object.active = false
const objectName = object.name as EntityTypeEnum
this.map.get(objectName).push(object)
object.active = false;
const objectName = object.name as EntityTypeEnum;
this.map.get(objectName).push(object);
}
}

View File

@@ -1,32 +1,32 @@
import { _decorator, resources, Asset } from 'cc'
import Singleton from '../Base/Singleton'
import { _decorator, resources, Asset } from "cc";
import Singleton from "../Base/Singleton";
export class ResourceManager extends Singleton {
static get Instance() {
return super.GetInstance<ResourceManager>()
return super.GetInstance<ResourceManager>();
}
loadRes<T extends Asset>(path: string, type: new (...args: any[]) => T) {
return new Promise<T>((resolve, reject) => {
resources.load(path, type, (err, res) => {
if (err) {
reject(err)
return
reject(err);
return;
}
resolve(res)
})
})
resolve(res);
});
});
}
loadDir<T extends Asset>(path: string, type: new (...args: any[]) => T) {
return new Promise<T[]>((resolve, reject) => {
resources.loadDir(path, type, (err, res) => {
if (err) {
reject(err)
return
reject(err);
return;
}
resolve(res)
})
})
resolve(res);
});
});
}
}

View File

@@ -1,192 +1,218 @@
import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director } from 'cc';
import { ActorManager } from '../Entity/Actor/ActorManager';
import DataManager from '../Global/DataManager';
import { JoyStickManager } from '../UI/JoyStickManager';
import { ResourceManager } from '../Global/ResourceManager';
import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from '../Enum';
import NetworkManager from '../Global/NetworkManager';
import ObjectPoolManager from '../Global/ObjectPoolManager';
import { BulletManager } from '../Entity/Bullet/BulletManager';
import { ApiMsgEnum, EntityTypeEnum, IMsgServerSync, InputTypeEnum } from '../Common';
import EventManager from '../Global/EventManager';
import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director } from "cc";
import { ActorManager } from "../Entity/Actor/ActorManager";
import DataManager from "../Global/DataManager";
import { JoyStickManager } from "../UI/JoyStickManager";
import { ResourceManager } from "../Global/ResourceManager";
import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from "../Enum";
import NetworkManager from "../Global/NetworkManager";
import ObjectPoolManager from "../Global/ObjectPoolManager";
import { BulletManager } from "../Entity/Bullet/BulletManager";
import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgServerSync, InputTypeEnum, toFixed } from "../Common";
import EventManager from "../Global/EventManager";
import { deepClone } from "../Utils";
const { ccclass } = _decorator;
@ccclass('BattleManager')
@ccclass("BattleManager")
export class BattleManager extends Component {
private stage: Node
private ui: Node
private shouldUpdate = false
private stage: Node;
private ui: Node;
private shouldUpdate = false;
private pendingMsg = [];
async start() {
//清空
this.clearGame()
async start() {
//清空
this.clearGame();
//资源加载和网络连接同步执行
await Promise.all([this.loadRes(), this.connectServer()])
//资源加载和网络连接同步执行
await Promise.all([this.loadRes(), this.connectServer()]);
this.initGame()
this.initGame();
// 在场景初始化完毕之前,卡主别的玩家,准备好以后再告知服务器,等所有玩家都准备好以后才开始,这里就不做了
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.handleSync, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameEnd, this.leaveGame, this);
EventManager.Instance.on(EventEnum.GameEnd, this.handleGameEnd, this)
// 在场景初始化完毕之前,卡主别的玩家,准备好以后再告知服务器,等所有玩家都准备好以后才开始,这里就不做了
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this);
EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this);
EventManager.Instance.on(EventEnum.GameEnd, this.handleGameEnd, this);
}
clearGame() {
//事件
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this);
EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this);
EventManager.Instance.off(EventEnum.GameEnd, this.handleGameEnd, this);
//数据
this.shouldUpdate = false;
ObjectPoolManager.Instance.reset();
DataManager.Instance.reset();
//节点
this.stage = DataManager.Instance.stage = this.node.getChildByName("Stage");
this.ui = this.node.getChildByName("UI");
this.stage.destroyAllChildren();
this.ui.destroyAllChildren();
}
async loadRes() {
const list = [];
for (const type in PrefabPathEnum) {
const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => {
DataManager.Instance.prefabMap.set(type, prefab);
});
list.push(p);
}
clearGame() {
//监听
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.handleSync, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameEnd, this.leaveGame, this);
EventManager.Instance.off(EventEnum.GameEnd, this.handleGameEnd, this)
//数据
this.shouldUpdate = false
ObjectPoolManager.Instance.reset()
DataManager.Instance.reset()
//节点
this.stage = DataManager.Instance.stage = this.node.getChildByName("Stage")
this.ui = this.node.getChildByName("UI")
this.stage.destroyAllChildren()
this.ui.destroyAllChildren()
for (const type in TexturePathEnum) {
const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => {
DataManager.Instance.textureMap.set(type, spriteFrames);
});
list.push(p);
}
await Promise.all(list);
}
async loadRes() {
const list = []
for (const type in PrefabPathEnum) {
const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => {
DataManager.Instance.prefabMap.set(type, prefab)
})
list.push(p)
}
for (const type in TexturePathEnum) {
const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => {
DataManager.Instance.textureMap.set(type, spriteFrames)
})
list.push(p)
}
await Promise.all(list)
async connectServer() {
if (!(await NetworkManager.Instance.connect().catch(() => false))) {
await new Promise((resolve) => setTimeout(resolve, 1000));
await this.connectServer();
}
}
async connectServer() {
if (!await NetworkManager.Instance.connect().catch(() => false)) {
await new Promise((resolve) => setTimeout(resolve, 1000))
await this.connectServer()
}
async initGame() {
this.initJoyStick();
this.initShoot();
this.initMap();
this.shouldUpdate = true;
}
initJoyStick() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.JoyStick);
const joySitck = instantiate(prefab);
joySitck.setParent(this.ui);
const jm = (DataManager.Instance.jm = joySitck.getComponent(JoyStickManager));
jm.init();
}
initShoot() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Shoot);
const shoot = instantiate(prefab);
shoot.setParent(this.ui);
}
initMap() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map1);
const map = instantiate(prefab);
map.setParent(this.stage);
}
update(dt: number) {
if (!this.shouldUpdate) {
return;
}
this.render();
this.tick(dt);
}
leaveGame() {
this.clearGame()
director.loadScene(SceneEnum.Hall);
tick(dt: number) {
this.tickPlayer(dt);
// this.tickGlobal(dt)
}
tickPlayer(dt: number) {
for (const p of DataManager.Instance.state.actors) {
const playerManager = DataManager.Instance.actorMap.get(p.id);
if (!playerManager) {
return;
}
playerManager.tick(dt);
}
}
async handleGameEnd() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameEnd, { rid: DataManager.Instance.roomInfo.id })
if (!success) {
console.log(error)
return;
}
// tickGlobal(dt: number) {
// DataManager.Instance.applyInput({
// type: InputTypeEnum.TimePast,
// dt: toFixed(dt),
// })
// }
render() {
this.renderPlayer();
this.renderBullet();
}
renderPlayer() {
for (const p of DataManager.Instance.state.actors) {
let actorManager = DataManager.Instance.actorMap.get(p.id);
if (!actorManager) {
const playerPrefab = DataManager.Instance.prefabMap.get(p.type);
const player = instantiate(playerPrefab);
player.setParent(this.stage);
actorManager = player.addComponent(ActorManager);
DataManager.Instance.actorMap.set(p.id, actorManager);
actorManager.init(p);
} else {
actorManager.render(p);
}
}
}
async initGame() {
this.initJoyStick()
this.initShoot()
this.initMap()
this.shouldUpdate = true
renderBullet() {
for (const b of DataManager.Instance.state.bullets) {
let bulletManager = DataManager.Instance.bulletMap.get(b.id);
if (!bulletManager) {
const bullet = ObjectPoolManager.Instance.get(b.type);
bulletManager = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager);
DataManager.Instance.bulletMap.set(b.id, bulletManager);
bulletManager.init(b);
} else {
bulletManager.render(b);
}
}
}
initJoyStick() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.JoyStick)
const joySitck = instantiate(prefab)
joySitck.setParent(this.ui)
const jm = DataManager.Instance.jm = joySitck.getComponent(JoyStickManager)
jm.init()
handleClientSync(input: IClientInput) {
const msg = {
frameId: DataManager.Instance.frameId++,
input,
};
NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg);
//移动才做预测,射击不做
if (input.type === InputTypeEnum.ActorMove) {
DataManager.Instance.applyInput(input);
this.pendingMsg.push(msg);
}
}
initShoot() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Shoot)
const shoot = instantiate(prefab)
shoot.setParent(this.ui)
listenServerSync({ lastFrameId, inputs }: IMsgServerSync) {
//回滚上次服务器状态
DataManager.Instance.state = DataManager.Instance.lastState;
//应用服务器输入
for (const input of inputs) {
DataManager.Instance.applyInput(input);
}
//记录最新的服务器状态
DataManager.Instance.lastState = deepClone(DataManager.Instance.state);
initMap() {
const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map1)
const map = instantiate(prefab)
map.setParent(this.stage)
//过滤本地输入
this.pendingMsg = this.pendingMsg.filter((msg) => msg.frameId > lastFrameId);
//应用本地输入
for (const msg of this.pendingMsg) {
DataManager.Instance.applyInput(msg.input);
}
}
update(dt: number) {
if (!this.shouldUpdate) {
return
}
this.render()
this.tick(dt)
}
tick(dt: number) {
this.tickPlayer(dt)
// this.tickGlobal(dt)
}
tickPlayer(dt: number) {
for (const p of DataManager.Instance.state.players) {
const playerManager = DataManager.Instance.actorMap.get(p.id)
if (!playerManager) {
return
}
playerManager.tick(dt)
}
}
// tickGlobal(dt: number) {
// NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, {
// input: {
// type: InputTypeEnum.TimePast,
// dt: toFixed(dt),
// }
// })
// }
render() {
this.renderPlayer()
this.renderBullet()
}
renderPlayer() {
for (const p of DataManager.Instance.state.players) {
let playerManager = DataManager.Instance.actorMap.get(p.id)
if (!playerManager) {
const playerPrefab = DataManager.Instance.prefabMap.get(p.type)
const player = instantiate(playerPrefab)
player.setParent(this.stage)
playerManager = player.addComponent(ActorManager)
DataManager.Instance.actorMap.set(p.id, playerManager)
playerManager.init(p)
} else {
playerManager.render(p)
}
}
}
renderBullet() {
for (const b of DataManager.Instance.state.bullets) {
let bulletManager = DataManager.Instance.bulletMap.get(b.id)
if (!bulletManager) {
const bullet = ObjectPoolManager.Instance.get(b.type)
bulletManager = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager)
DataManager.Instance.bulletMap.set(b.id, bulletManager)
bulletManager.init(b)
} else {
bulletManager.render(b)
}
}
}
handleSync(inputs: IMsgServerSync) {
for (const input of inputs) {
DataManager.Instance.applyInput(input)
}
async handleGameEnd() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameEnd, { rid: DataManager.Instance.roomInfo.id });
if (!success) {
console.log(error);
return;
}
}
listenGameEnd() {
this.clearGame();
director.loadScene(SceneEnum.Hall);
}
}

View File

@@ -1,121 +1,122 @@
import { _decorator, Component, Node, Prefab, director, instantiate } from 'cc';
import { ApiMsgEnum, IApiPlayerListRes, IApiRoomListRes, IMsgPlayerList, IMsgRoomList } from '../Common';
import { EventEnum, SceneEnum } from '../Enum';
import DataManager from '../Global/DataManager';
import EventManager from '../Global/EventManager';
import NetworkManager from '../Global/NetworkManager';
import { PlayerManager } from '../UI/PlayerManager';
import { RoomManager } from '../UI/RoomManager';
import { _decorator, Component, Node, Prefab, director, instantiate } from "cc";
import { ApiMsgEnum, IApiPlayerListRes, IApiRoomListRes, IMsgPlayerList, IMsgRoomList } from "../Common";
import { EventEnum, SceneEnum } from "../Enum";
import DataManager from "../Global/DataManager";
import EventManager from "../Global/EventManager";
import NetworkManager from "../Global/NetworkManager";
import { PlayerManager } from "../UI/PlayerManager";
import { RoomManager } from "../UI/RoomManager";
const { ccclass, property } = _decorator;
@ccclass('HallManager')
@ccclass("HallManager")
export class HallManager extends Component {
@property(Node)
playerContainer: Node = null;
@property(Node)
playerContainer: Node = null;
@property(Prefab)
playerPrefab: Prefab = null;
@property(Prefab)
playerPrefab: Prefab = null;
@property(Node)
roomContainer: Node = null;
@property(Node)
roomContainer: Node = null;
@property(Prefab)
roomPrefab: Prefab = null;
@property(Prefab)
roomPrefab: Prefab = null;
onLoad() {
director.preloadScene(SceneEnum.Room);
EventManager.Instance.on(EventEnum.RoomJoin, this.joinRoom, this)
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this);
onLoad() {
director.preloadScene(SceneEnum.Room);
EventManager.Instance.on(EventEnum.RoomJoin, this.handleJoinRoom, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this);
}
onDestroy() {
EventManager.Instance.off(EventEnum.RoomJoin, this.handleJoinRoom, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this);
}
start() {
this.playerContainer.destroyAllChildren();
this.roomContainer.destroyAllChildren();
this.getPlayers();
this.getRooms();
}
async getPlayers() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerList, {});
if (!success) {
console.log(error);
return;
}
onDestroy() {
EventManager.Instance.off(EventEnum.RoomJoin, this.joinRoom, this)
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgPlayerList, this.renderPlayers, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoomList, this.renderRooms, this);
this.renderPlayers(res);
}
renderPlayers({ list }: IApiPlayerListRes | IMsgPlayerList) {
for (const item of this.playerContainer.children) {
item.active = false;
}
while (this.playerContainer.children.length < list.length) {
const playerItem = instantiate(this.playerPrefab);
playerItem.active = false;
playerItem.setParent(this.playerContainer);
}
start() {
this.getPlayers()
this.getRooms()
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.playerContainer.children[i];
const playerManager = node.getComponent(PlayerManager);
playerManager.init(data);
}
}
async getRooms() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomList, {});
if (!success) {
console.log(error);
return;
}
async getPlayers() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerList, {});
if (!success) {
console.log(error)
return;
}
this.renderRooms(res);
}
this.renderPlayers(res)
renderRooms = ({ list }: IApiRoomListRes | IMsgRoomList) => {
for (const item of this.roomContainer.children) {
item.active = false;
}
while (this.roomContainer.children.length < list.length) {
const roomItem = instantiate(this.roomPrefab);
roomItem.active = false;
roomItem.setParent(this.roomContainer);
}
renderPlayers = ({ list }: IApiPlayerListRes | IMsgPlayerList) => {
for (const item of this.playerContainer.children) {
item.active = false
}
while (this.playerContainer.children.length < list.length) {
const playerItem = instantiate(this.playerPrefab);
playerItem.active = false
playerItem.setParent(this.playerContainer)
}
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.roomContainer.children[i];
const roomItemManager = node.getComponent(RoomManager);
roomItemManager.init(data);
}
};
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.playerContainer.children[i]
const playerItemManager = node.getComponent(PlayerManager)
playerItemManager.init(data)
}
async handleCreateRoom() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomCreate, {});
if (!success) {
console.log(error);
return;
}
async getRooms() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomList, {});
if (!success) {
console.log(error)
return;
}
DataManager.Instance.roomInfo = res.room;
director.loadScene(SceneEnum.Room);
}
this.renderRooms(res)
async handleJoinRoom(rid: number) {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomJoin, { rid });
if (!success) {
console.log(error);
return;
}
renderRooms = ({ list }: IApiRoomListRes | IMsgRoomList) => {
for (const item of this.roomContainer.children) {
item.active = false
}
while (this.roomContainer.children.length < list.length) {
const roomItem = instantiate(this.roomPrefab);
roomItem.active = false
roomItem.setParent(this.roomContainer)
}
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.roomContainer.children[i]
const roomItemManager = node.getComponent(RoomManager)
roomItemManager.init(data)
}
}
async createRoom() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomCreate, {});
if (!success) {
console.log(error)
return;
}
DataManager.Instance.roomInfo = res.room
director.loadScene(SceneEnum.Room);
}
async joinRoom(rid: number) {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomJoin, { rid });
if (!success) {
console.log(error)
return;
}
DataManager.Instance.roomInfo = res.room
director.loadScene(SceneEnum.Room);
}
DataManager.Instance.roomInfo = res.room;
director.loadScene(SceneEnum.Room);
}
}

View File

@@ -1,45 +1,44 @@
import { _decorator, Component, EditBox, director } from 'cc';
import { ApiMsgEnum } from '../Common';
import { SceneEnum } from '../Enum';
import DataManager from '../Global/DataManager';
import NetworkManager from '../Global/NetworkManager';
import { _decorator, Component, EditBox, director } from "cc";
import { ApiMsgEnum } from "../Common";
import { SceneEnum } from "../Enum";
import DataManager from "../Global/DataManager";
import NetworkManager from "../Global/NetworkManager";
const { ccclass, property } = _decorator;
@ccclass('LoginManager')
@ccclass("LoginManager")
export class LoginManager extends Component {
input: EditBox
input: EditBox;
onLoad() {
this.input = this.node.getChildByName('Input').getComponent(EditBox)
director.preloadScene(SceneEnum.Hall);
onLoad() {
this.input = this.node.getChildByName("Input").getComponent(EditBox);
director.preloadScene(SceneEnum.Hall);
}
async start() {
await NetworkManager.Instance.connect();
console.log("服务连接成功!");
}
async handleClick() {
if (!NetworkManager.Instance.isConnected) {
console.log("未连接!");
await NetworkManager.Instance.connect();
}
const nickname = this.input.string;
if (!nickname) {
console.log("请输入昵称!");
return;
}
let { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerJoin, {
nickname,
});
if (!success) {
console.log(error);
return;
}
async start() {
await NetworkManager.Instance.connect();
console.log("服务连接成功!");
}
async handleClick() {
if (!NetworkManager.Instance.isConnected) {
console.log("未连接!");
await NetworkManager.Instance.connect();
}
const nickname = this.input.string;
if (!nickname) {
console.log("请输入昵称!")
return;
}
let { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiPlayerJoin, {
nickname,
});
if (!success) {
console.log(error);
return;
}
DataManager.Instance.myPlayerId = res.player.id;
director.loadScene(SceneEnum.Hall);
}
DataManager.Instance.myPlayerId = res.player.id;
director.loadScene(SceneEnum.Hall);
}
}

View File

@@ -1,76 +1,77 @@
import { _decorator, Component, Node, Prefab, director, instantiate } from 'cc';
import { ApiMsgEnum, IMsgGameStart, IMsgRoom } from '../Common';
import { SceneEnum } from '../Enum';
import DataManager from '../Global/DataManager';
import NetworkManager from '../Global/NetworkManager';
import { PlayerManager } from '../UI/PlayerManager';
import { _decorator, Component, Node, Prefab, director, instantiate } from "cc";
import { ApiMsgEnum, IMsgGameStart, IMsgRoom } from "../Common";
import { SceneEnum } from "../Enum";
import DataManager from "../Global/DataManager";
import NetworkManager from "../Global/NetworkManager";
import { PlayerManager } from "../UI/PlayerManager";
import { deepClone } from "../Utils";
const { ccclass, property } = _decorator;
@ccclass('RoomManager')
@ccclass("RoomManager")
export class RoomManager extends Component {
@property(Node)
playerContainer: Node = null;
@property(Node)
playerContainer: Node = null;
@property(Prefab)
playerPrefab: Prefab = null;
@property(Prefab)
playerPrefab: Prefab = null;
onLoad() {
director.preloadScene(SceneEnum.Battle);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this);
onLoad() {
director.preloadScene(SceneEnum.Battle);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this);
NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this);
}
onDestroy() {
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this);
}
async start() {
this.renderPlayers({
room: DataManager.Instance.roomInfo,
});
}
renderPlayers({ room: { players: list } }: IMsgRoom) {
for (const item of this.playerContainer.children) {
item.active = false;
}
while (this.playerContainer.children.length < list.length) {
const playerItem = instantiate(this.playerPrefab);
playerItem.active = false;
playerItem.setParent(this.playerContainer);
}
onDestroy() {
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgRoom, this.renderPlayers, this);
NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameStart, this.handleGameStart, this);
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.playerContainer.children[i];
const playerItemManager = node.getComponent(PlayerManager);
playerItemManager.init(data);
}
}
async handleLeave() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomLeave, {});
if (!success) {
console.log(error);
return;
}
async start() {
this.renderPlayers({
room: DataManager.Instance.roomInfo
})
DataManager.Instance.roomInfo = null;
director.loadScene(SceneEnum.Hall);
}
async handleStart() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameStart, { rid: DataManager.Instance.roomInfo.id });
if (!success) {
console.log(error);
return;
}
}
renderPlayers({ room: { players: list } }: IMsgRoom) {
for (const item of this.playerContainer.children) {
item.active = false
}
while (this.playerContainer.children.length < list.length) {
const playerItem = instantiate(this.playerPrefab);
playerItem.active = false
playerItem.setParent(this.playerContainer)
}
for (let i = 0; i < list.length; i++) {
const data = list[i];
const node = this.playerContainer.children[i]
const playerItemManager = node.getComponent(PlayerManager)
playerItemManager.init(data)
}
}
async handleLeave() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiRoomLeave, {});
if (!success) {
console.log(error)
return;
}
DataManager.Instance.roomInfo = null
director.loadScene(SceneEnum.Hall);
}
async handleStart() {
const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameStart, { rid: DataManager.Instance.roomInfo.id });
if (!success) {
console.log(error)
return;
}
}
handleGameStart({ state }: IMsgGameStart) {
DataManager.Instance.state = state
director.loadScene(SceneEnum.Battle);
}
handleGameStart({ state }: IMsgGameStart) {
DataManager.Instance.state = state;
DataManager.Instance.lastState = deepClone(DataManager.Instance.state);
director.loadScene(SceneEnum.Battle);
}
}

View File

@@ -1,57 +1,55 @@
import { _decorator, Component, Node, input, Input, EventTouch, Vec2, Vec3, UITransform } from 'cc';
import { _decorator, Component, Node, input, Input, EventTouch, Vec2, Vec3, UITransform } from "cc";
const { ccclass, property } = _decorator;
@ccclass('JoyStickManager')
@ccclass("JoyStickManager")
export class JoyStickManager extends Component {
input: Vec2 = Vec2.ZERO
input: Vec2 = Vec2.ZERO;
private body: Node
private stick: Node
private touchStartPos: Vec2
private defaultPos: Vec2
private radius: number = 0
private body: Node;
private stick: Node;
private touchStartPos: Vec2;
private defaultPos: Vec2;
private radius: number = 0;
init() {
this.body = this.node.getChildByName("Body")
this.stick = this.body.getChildByName("Stick")
const { x, y } = this.body.position
this.defaultPos = new Vec2(x, y)
this.radius = this.body.getComponent(UITransform).contentSize.x / 2
init() {
this.body = this.node.getChildByName("Body");
this.stick = this.body.getChildByName("Stick");
const { x, y } = this.body.position;
this.defaultPos = new Vec2(x, y);
this.radius = this.body.getComponent(UITransform).contentSize.x / 2;
input.on(Input.EventType.TOUCH_START, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
input.on(Input.EventType.TOUCH_START, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
}
onDestroy() {
input.off(Input.EventType.TOUCH_START, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
}
onTouchMove(e: EventTouch) {
const touchPos = e.touch.getUILocation();
if (!this.touchStartPos) {
this.touchStartPos = touchPos.clone();
this.body.setPosition(this.touchStartPos.x, this.touchStartPos.y);
}
onDestroy() {
input.off(Input.EventType.TOUCH_START, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
const stickPos = new Vec2(touchPos.x - this.touchStartPos.x, touchPos.y - this.touchStartPos.y);
const len = stickPos.length();
if (len > this.radius) {
stickPos.multiplyScalar(this.radius / len);
}
onTouchMove(e: EventTouch) {
const touchPos = e.touch.getUILocation()
if (!this.touchStartPos) {
this.touchStartPos = touchPos.clone()
this.body.setPosition(this.touchStartPos.x, this.touchStartPos.y);
}
this.stick.setPosition(stickPos.x, stickPos.y);
this.input = stickPos.clone().normalize();
}
const stickPos = new Vec2(touchPos.x - this.touchStartPos.x, touchPos.y - this.touchStartPos.y)
const len = stickPos.length()
if (len > this.radius) {
stickPos.multiplyScalar(this.radius / len)
}
this.stick.setPosition(stickPos.x, stickPos.y)
this.input = stickPos.clone().normalize()
}
onTouchEnd() {
this.body.setPosition(this.defaultPos.x, this.defaultPos.y)
this.stick.setPosition(0, 0)
this.touchStartPos = undefined
this.input = Vec2.ZERO
}
onTouchEnd() {
this.body.setPosition(this.defaultPos.x, this.defaultPos.y);
this.stick.setPosition(0, 0);
this.touchStartPos = undefined;
this.input = Vec2.ZERO;
}
}

View File

@@ -1,22 +1,19 @@
import { _decorator, Component, Node, Label } from 'cc';
import { EventEnum } from '../Enum';
import DataManager from '../Global/DataManager';
import EventManager from '../Global/EventManager';
import { _decorator, Component, Node, Label } from "cc";
import DataManager from "../Global/DataManager";
const { ccclass, property } = _decorator;
@ccclass('PlayerManager')
@ccclass("PlayerManager")
export class PlayerManager extends Component {
init({ id, nickname, rid }: { id: number, nickname: string, rid: number }) {
const label = this.getComponent(Label)
let str = nickname
init({ id, nickname, rid }: { id: number; nickname: string; rid: number }) {
const label = this.getComponent(Label);
let str = nickname;
if (DataManager.Instance.myPlayerId === id) {
str += `(我)`
str += `(我)`;
}
if (rid !== -1) {
str += `(房间${rid})`
str += `(房间${rid})`;
}
label.string = str
this.node.active = true
label.string = str;
this.node.active = true;
}
}

View File

@@ -1,20 +1,19 @@
import { _decorator, Component, Node, Label } from 'cc';
import { EventEnum } from '../Enum';
import EventManager from '../Global/EventManager';
import { _decorator, Component, Node, Label } from "cc";
import { EventEnum } from "../Enum";
import EventManager from "../Global/EventManager";
const { ccclass, property } = _decorator;
@ccclass('RoomManager')
@ccclass("RoomManager")
export class RoomManager extends Component {
id: number
init({ id, players }: { id: number, players: Array<{ id: number, nickname: string }> }) {
this.id = id
const label = this.getComponent(Label)
label.string = `房间id:${id},当前人数:${players.length}`
this.node.active = true
id: number;
init({ id, players }: { id: number; players: Array<{ id: number; nickname: string }> }) {
this.id = id;
const label = this.getComponent(Label);
label.string = `房间id:${id},当前人数:${players.length}`;
this.node.active = true;
}
handleClick() {
EventManager.Instance.emit(EventEnum.RoomJoin, this.id)
EventManager.Instance.emit(EventEnum.RoomJoin, this.id);
}
}

View File

@@ -1,13 +1,11 @@
import { _decorator, Component, Node } from 'cc';
import { EventEnum } from '../Enum';
import EventManager from '../Global/EventManager';
import { _decorator, Component, Node } from "cc";
import { EventEnum } from "../Enum";
import EventManager from "../Global/EventManager";
const { ccclass, property } = _decorator;
@ccclass('ShootManager')
@ccclass("ShootManager")
export class ShootManager extends Component {
shoot() {
EventManager.Instance.emit(EventEnum.WeaponShoot)
}
shoot() {
EventManager.Instance.emit(EventEnum.WeaponShoot);
}
}

View File

@@ -1,11 +1,25 @@
import { SpriteFrame } from "cc"
import { ApiMsgEnum, InputTypeEnum, strdecode } from "../Common"
import { SpriteFrame } from "cc";
const INDEX_REG = /\((\d+)\)/
const INDEX_REG = /\((\d+)\)/;
const getNumberWithinString = (str: string) => parseInt(str.match(INDEX_REG)?.[1] || '0')
const getNumberWithinString = (str: string) => parseInt(str.match(INDEX_REG)?.[1] || "0");
export const sortSpriteFrame = (spriteFrame: Array<SpriteFrame>) =>
spriteFrame.sort((a, b) => getNumberWithinString(a.name) - getNumberWithinString(b.name))
spriteFrame.sort((a, b) => getNumberWithinString(a.name) - getNumberWithinString(b.name));
export const rad2Angle = (rad: number) => rad / Math.PI * 180
export const rad2Angle = (rad: number) => (rad / Math.PI) * 180;
export const deepClone = (obj: any) => {
if (typeof obj !== "object" || obj === null) {
return obj;
}
const res = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res[key] = deepClone(obj[key]);
}
}
return res;
};