update
This commit is contained in:
173
apps/client/assets/Scripts/Global/DataManager.ts
Normal file
173
apps/client/assets/Scripts/Global/DataManager.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { Node, Prefab, SpriteFrame } from 'cc'
|
||||
import Singleton from '../Base/Singleton'
|
||||
import { BulletManager } from '../Entity/Bullet/BulletManager'
|
||||
import { PlayerManager } from '../Entity/Player/PlayerManager'
|
||||
import { EntityTypeEnum, EventEnum, InputType } from '../Enum'
|
||||
import { JoyStickManager } from '../UI/JoyStickManager'
|
||||
import EventManager from './EventManager'
|
||||
import { IData } from './NetworkManager'
|
||||
|
||||
export type IPlayer = Pick<PlayerManager, 'id' | 'nickname' | 'hp' | 'position' | 'direction' | 'type' | 'weaponType' | 'bulletType'>
|
||||
export type IBullet = Pick<BulletManager, 'id' | 'owner' | 'position' | 'direction' | 'type'>
|
||||
|
||||
const PLAYER_SPEED = 100
|
||||
const BULLET_SPEED = 600
|
||||
|
||||
const WEAPON_DAMAGE = 5
|
||||
|
||||
const PLAYER_RADIUS = 50
|
||||
const BULLET_RADIUS = 10
|
||||
|
||||
export interface IVec2 {
|
||||
x: number;
|
||||
y: number
|
||||
}
|
||||
|
||||
interface IState {
|
||||
players: IPlayer[],
|
||||
bullets: IBullet[],
|
||||
nextBulletId: number
|
||||
}
|
||||
|
||||
interface IPlayerMove {
|
||||
type: InputType.PlayerMove
|
||||
id: number;
|
||||
direction: IVec2;
|
||||
dt: number;
|
||||
}
|
||||
|
||||
interface IWeaponShoot {
|
||||
type: InputType.WeaponShoot
|
||||
owner: number;
|
||||
position: IVec2;
|
||||
direction: IVec2;
|
||||
}
|
||||
|
||||
interface ITimePast {
|
||||
type: InputType.TimePast;
|
||||
dt: number
|
||||
}
|
||||
|
||||
export default class DataManager extends Singleton {
|
||||
static get Instance() {
|
||||
return super.GetInstance<DataManager>()
|
||||
}
|
||||
|
||||
stage: Node
|
||||
jm: JoyStickManager
|
||||
|
||||
prefabMap: Map<string, Prefab> = new Map()
|
||||
textureMap: Map<string, SpriteFrame[]> = new Map()
|
||||
|
||||
playerMap: Map<number, PlayerManager> = new Map()
|
||||
bulletMap: Map<number, BulletManager> = new Map()
|
||||
|
||||
myPlayerId = 1
|
||||
roomInfo: IData
|
||||
mapSize = {
|
||||
x: 960,
|
||||
y: 640,
|
||||
}
|
||||
|
||||
state: IState = {
|
||||
players: [{
|
||||
id: 2,
|
||||
nickname: "哈哈1",
|
||||
position: {
|
||||
x: -200,
|
||||
y: -200
|
||||
},
|
||||
direction: {
|
||||
x: 1,
|
||||
y: 0
|
||||
},
|
||||
hp: 100,
|
||||
type: EntityTypeEnum.Player1,
|
||||
weaponType: EntityTypeEnum.Weapon1,
|
||||
bulletType: EntityTypeEnum.Bullet1,
|
||||
}, {
|
||||
id: 1,
|
||||
nickname: "哈哈2",
|
||||
position: {
|
||||
x: 200,
|
||||
y: 200
|
||||
},
|
||||
direction: {
|
||||
x: 0,
|
||||
y: -1
|
||||
},
|
||||
hp: 100,
|
||||
type: EntityTypeEnum.Player2,
|
||||
weaponType: EntityTypeEnum.Weapon2,
|
||||
bulletType: EntityTypeEnum.Bullet2,
|
||||
}],
|
||||
bullets: [],
|
||||
nextBulletId: 1
|
||||
}
|
||||
|
||||
applyInput(input: IPlayerMove | IWeaponShoot | ITimePast) {
|
||||
switch (input.type) {
|
||||
case InputType.PlayerMove: {
|
||||
const { direction: { x, y }, dt, id } = input
|
||||
const player = this.state.players.find(e => e.id === id)
|
||||
if (!player) {
|
||||
return
|
||||
}
|
||||
|
||||
player.position.x += x * PLAYER_SPEED * dt
|
||||
player.position.y += y * PLAYER_SPEED * dt
|
||||
player.direction = { x, y }
|
||||
break
|
||||
}
|
||||
|
||||
case InputType.WeaponShoot: {
|
||||
const { owner, position, direction } = input
|
||||
const bullet: IBullet = {
|
||||
id: this.state.nextBulletId++,
|
||||
owner,
|
||||
position,
|
||||
direction,
|
||||
type: this.playerMap.get(owner).bulletType
|
||||
}
|
||||
this.state.bullets.push(bullet)
|
||||
|
||||
EventManager.Instance.emit(EventEnum.BulletBorn, owner)
|
||||
break
|
||||
}
|
||||
case InputType.TimePast: {
|
||||
const { dt } = input
|
||||
const { bullets, players } = 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) {
|
||||
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
|
||||
x: (player.position.x + bullet.position.x) / 2,
|
||||
y: (player.position.y + bullet.position.y) / 2,
|
||||
})
|
||||
|
||||
player.hp -= WEAPON_DAMAGE
|
||||
bullets.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (Math.abs(bullet.position.x) > this.mapSize.x / 2 || Math.abs(bullet.position.y) > this.mapSize.y / 2) {
|
||||
EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
|
||||
x: bullet.position.x,
|
||||
y: bullet.position.y,
|
||||
})
|
||||
bullets.splice(i, 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (const bullet of this.state.bullets) {
|
||||
bullet.position.x += bullet.direction.x * BULLET_SPEED * dt
|
||||
bullet.position.y += bullet.direction.y * BULLET_SPEED * dt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
apps/client/assets/Scripts/Global/DataManager.ts.meta
Normal file
9
apps/client/assets/Scripts/Global/DataManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1682ed16-4e4f-46b3-8aca-92baecd30b9d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
42
apps/client/assets/Scripts/Global/EventManager.ts
Normal file
42
apps/client/assets/Scripts/Global/EventManager.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import Singleton from '../Base/Singleton'
|
||||
import { EventEnum } from '../Enum';
|
||||
|
||||
interface IItem {
|
||||
func: Function;
|
||||
ctx: unknown;
|
||||
}
|
||||
|
||||
export default class EventManager extends Singleton {
|
||||
static get Instance() {
|
||||
return super.GetInstance<EventManager>();
|
||||
}
|
||||
|
||||
private map: Map<EventEnum, Array<IItem>> = new Map();
|
||||
|
||||
on(event: EventEnum, func: Function, ctx?: unknown) {
|
||||
if (this.map.has(event)) {
|
||||
this.map.get(event).push({ func, ctx });
|
||||
} else {
|
||||
this.map.set(event, [{ func, ctx }]);
|
||||
}
|
||||
}
|
||||
|
||||
off(event: EventEnum, func: Function, ctx?: unknown) {
|
||||
if (this.map.has(event)) {
|
||||
const index = this.map.get(event).findIndex(i => func === i.func && i.ctx === ctx);
|
||||
index > -1 && this.map.get(event).splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
emit(event: EventEnum, ...params: unknown[]) {
|
||||
if (this.map.has(event)) {
|
||||
this.map.get(event).forEach(({ func, ctx }) => {
|
||||
ctx ? func.apply(ctx, params) : func(...params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
9
apps/client/assets/Scripts/Global/EventManager.ts.meta
Normal file
9
apps/client/assets/Scripts/Global/EventManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2aa2e7de-b014-4ee7-a537-4bc99dbf9164",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
117
apps/client/assets/Scripts/Global/NetworkManager.ts
Normal file
117
apps/client/assets/Scripts/Global/NetworkManager.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import Singleton from '../Base/Singleton'
|
||||
|
||||
const TIMEOUT = 5000
|
||||
|
||||
export type IData = Record<string, any>
|
||||
|
||||
export interface ICallApiRet {
|
||||
success: boolean;
|
||||
error?: Error;
|
||||
res?: IData
|
||||
}
|
||||
|
||||
export enum ApiMsgEnum {
|
||||
ApiPlayerList = 'ApiPlayerList',
|
||||
ApiPlayerJoin = 'ApiPlayerJoin',
|
||||
ApiRoomList = 'ApiRoomList',
|
||||
ApiRoomCreate = 'ApiRoomCreate',
|
||||
ApiRoomJoin = 'ApiRoomJoin',
|
||||
ApiRoomLeave = 'ApiRoomLeave',
|
||||
MsgPlayerList = 'MsgPlayerList',
|
||||
MsgRoomList = 'MsgRoomList',
|
||||
MsgRoom = 'MsgRoom',
|
||||
}
|
||||
|
||||
export default class NetworkManager extends Singleton {
|
||||
static get Instance() {
|
||||
return super.GetInstance<NetworkManager>()
|
||||
}
|
||||
|
||||
ws: WebSocket
|
||||
port = 8888
|
||||
cbs: Map<string, Function[]> = new Map()
|
||||
isConnected = false
|
||||
|
||||
connect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ws = new WebSocket(`ws://localhost:${this.port}`)
|
||||
this.ws.onopen = () => {
|
||||
console.log("ws onopen")
|
||||
this.isConnected = true
|
||||
resolve(true)
|
||||
}
|
||||
|
||||
this.ws.onerror = () => {
|
||||
this.isConnected = false
|
||||
reject("ws onerror")
|
||||
}
|
||||
|
||||
this.ws.onclose = () => {
|
||||
this.isConnected = false
|
||||
reject("ws onclose")
|
||||
}
|
||||
|
||||
this.ws.onmessage = (e) => {
|
||||
try {
|
||||
const json = JSON.parse(e.data)
|
||||
const { name, data } = json
|
||||
try {
|
||||
if (this.cbs.has(name) && this.cbs.get(name).length) {
|
||||
console.log(json);
|
||||
this.cbs.get(name).forEach(cb => cb(data))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("this.cbs.get(name).forEach(cb => cb(restData))", error)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('解析失败,不是合法的JSON格式', error)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
sendMsg(name: string, data: IData) {
|
||||
this.ws.send(JSON.stringify({ name, data }))
|
||||
}
|
||||
|
||||
listenMsg(name: string, cb: Function) {
|
||||
if (this.cbs.has(name)) {
|
||||
this.cbs.get(name).push(cb)
|
||||
} else {
|
||||
this.cbs.set(name, [cb])
|
||||
}
|
||||
}
|
||||
|
||||
unlistenMsg(name: string, cb: Function) {
|
||||
if (this.cbs.has(name)) {
|
||||
const index = this.cbs.get(name).indexOf(cb)
|
||||
index > -1 && this.cbs.get(name).splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
callApi(name: string, data: IData) {
|
||||
return new Promise<ICallApiRet>((resolve) => {
|
||||
try {
|
||||
// 超时处理
|
||||
const timer = setTimeout(() => {
|
||||
resolve({ success: false, error: new Error('timeout') })
|
||||
this.unlistenMsg(name, cb)
|
||||
}, TIMEOUT)
|
||||
|
||||
// 回调处理
|
||||
const cb = (res: ICallApiRet) => {
|
||||
resolve(res)
|
||||
clearTimeout(timer)
|
||||
this.unlistenMsg(name, cb)
|
||||
}
|
||||
this.listenMsg(name, cb)
|
||||
|
||||
this.ws.send(JSON.stringify({ name, data }))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
resolve({ success: false, error: error as Error })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
9
apps/client/assets/Scripts/Global/NetworkManager.ts.meta
Normal file
9
apps/client/assets/Scripts/Global/NetworkManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "588dfbf5-0456-472f-8382-746392fb1a06",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
50
apps/client/assets/Scripts/Global/ObjectPoolManager.ts
Normal file
50
apps/client/assets/Scripts/Global/ObjectPoolManager.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import Singleton from '../Base/Singleton'
|
||||
import { instantiate, Node } from 'cc'
|
||||
import { EntityTypeEnum } from '../Enum'
|
||||
import DataManager from './DataManager'
|
||||
|
||||
export default class ObjectPoolManager extends Singleton {
|
||||
static get Instance() {
|
||||
return super.GetInstance<ObjectPoolManager>()
|
||||
}
|
||||
|
||||
private objectPool: Node = null
|
||||
private map: Map<EntityTypeEnum, Node[]> = new Map()
|
||||
|
||||
private getContainerName(objectName: EntityTypeEnum) {
|
||||
return objectName + 'Pool'
|
||||
}
|
||||
|
||||
get(objectName: EntityTypeEnum) {
|
||||
if (this.objectPool === null) {
|
||||
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)
|
||||
}
|
||||
|
||||
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)))
|
||||
} else {
|
||||
node = nodes.pop()
|
||||
}
|
||||
node.active = true
|
||||
return node
|
||||
}
|
||||
|
||||
ret(object: Node) {
|
||||
object.active = false
|
||||
const objectName = object.name as EntityTypeEnum
|
||||
this.map.get(objectName).push(object)
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "01a37612-1a4a-45ee-8be8-3e3360996442",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
32
apps/client/assets/Scripts/Global/ResourceManager.ts
Normal file
32
apps/client/assets/Scripts/Global/ResourceManager.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { _decorator, resources, Asset } from 'cc'
|
||||
import Singleton from '../Base/Singleton'
|
||||
|
||||
export class ResourceManager extends Singleton {
|
||||
static get Instance() {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6278727d-3641-46d1-ae43-52b7829402d1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
Reference in New Issue
Block a user