背景
引入了ecs后,就开始用ecs的方法将我们的游戏逻辑布置到画板上去。原先后端是也是ts,用的是tsrpc框架,属于全栈,但使用了一段时间,虽然在加密和前后端统一上效果还是可以的。但是因为前后端传输协议的字段定的太死,测试环境和正式环境的过渡阶段非常难受。后端没法根据版本号,进行前端新老版本的结构输出,所以继续沿用php,怎么说呢,之前想上各种前沿的技术,生怕游戏流量大顶不住,可是第一版的上线情况来说,还是现实一点,毕竟是个人开发,高并发高流量的那些别老是自己一个人yy,用自己最熟悉的技术去恰当的完成就可以了。不怕工具多,就怕工具不会用,笨手笨脚还把手给砸了。
目标
开始游戏后显示一个是登录页,并根据当前的平台,自动登录。后端用thinkphp6+ swoole。
用ecs的组件模式,实现一个自动登录的PlayerLoginComp组件
开发过程
1、布置一个gui的预制页面
2、初始化系统,新增组件
PS:当前为开发日志时的第一版,后面因ecs架构的调整,已做优化,末尾附优化后的部分代码
protected initGui() {
Logger.logBusiness('开始')
// 新增我们的系统
ooxh.systemManager.addSystem(new NodeViewSystem());
ooxh.systemManager.addSystem(new PlayerStateSystem());
ooxh.systemManager.addSystem(new PlayerLoginSystem());
this.createPlayerEntityAndInit()
this.createGateEntityAndInit()
}
private createGateEntityAndInit() {
// 创建一个gate实体
const gate_entity = ooxh.entityManager.createEntity();
// 加入视图组件
let _NodeViewComp = PooledComponent.requestComponent(NodeViewComp)
_NodeViewComp.prefabPath = 'gui/gate_index'
_NodeViewComp.callback = () => {
console.log('_NodeViewComp加载显示完成')
}
gate_entity.attachComponent(_NodeViewComp);
console.log(gate_entity)
}
private createPlayerEntityAndInit() {
// 创建一个player实体
const player_entity = ooxh.entityManager.createEntity();
// 玩家状态组件
let _PlayerStateComp = PooledComponent.requestComponent(PlayerStateComp)
player_entity.attachComponent(_PlayerStateComp);
// 加入登录组件
let _PlayerLoginComp = PooledComponent.requestComponent(PlayerLoginComp)
player_entity.attachComponent(_PlayerLoginComp);
setTimeout(() => {
console.log(player_entity)
}, 3000)
}
3、实现玩家登录组件PlayerLoginComp
export class PlayerLoginSystem {
// 系统定期触发
apply(entity: Entity) {
}
static doOnce(entity: Entity) {
const comp = entity.getComponent(PlayerLoginComp);
if (comp && comp.isGetting == false) {
comp.isGetting = true
console.log('comp.isGetting = true')
this.autoLogin(comp, (isOk) => {
if (isOk) {
const stateComp = entity.getComponent(PlayerStateComp);
if (stateComp) {
stateComp.openid = comp.openid
stateComp.server_no = comp.server_no
stateComp.platform = comp.platform
}
comp.callback && comp.callback(true)
} else {
comp.callback && comp.callback(false)
}
comp.entity.detachComponent(PlayerLoginComp)
})
}
}
static async autoLogin(comp: PlayerLoginComp, callback: Function) {
var that = this
if (WECHAT) {
// 进行登录
wx.login({
success: function (res) {
comp.platform = Platform.Weixin
comp.resObject = res
that.apiLogin(comp, callback)
}
});
} else if (BYTEDANCE) {
} else {
comp.platform = Platform.H5
that.apiLogin(comp, callback)
}
}
static async apiLogin(comp: PlayerLoginComp, callback: Function) {
console.log('apiLogin')
switch (comp.platform) {
case Platform.H5:
comp.openid = 'testopenid1212'
comp.server_no = 'h001'
comp.token = 'xxxx'
callback && callback(true)
break;
case Platform.Weixin:
let code = typeof comp.resObject.code == undefined ? '' : comp.resObject.code
let anonymousCode = ''
HttpUtil.Get('/appletapi/jianchuqiao/loginByCode',
{
code: code,
platform: comp.platform,
anonymousCode: anonymousCode
},
(res) => {
console.log('HttpUtil.Get res', res)
if (res.code == 200) {
comp.openid = res.data.openid
comp.token = res.data.token
comp.server_no = res.data.server_no
callback && callback(true)
} else {
callback && callback(false)
}
})
break;
}
}
}
export class PlayerLoginComp extends PooledComponent {
isGetting: boolean = false
callback: Function = null
openid: string = ''
server_no: string = ''
platform: string = ''
resObject: any = null
token: string = ''
reset() {
this.isGetting = false
this.callback = null
this.openid = ''
this.server_no = ''
this.platform = ''
this.resObject = null
this.token = ''
}
onAttach(entity) {
this.entity = entity
console.log('实体', entity, '挂载了 PlayerLoginComp')
PlayerLoginSystem.doOnce(entity)
}
onDetach(entity) {
this.entity = null
console.log('实体', entity, '移除了 PlayerLoginComp')
}
}
运行测试记录
效果预期的达成情况
基本完成登录,后面还差一点体力值和金币值的返回,这个到后面具体游戏业务的时候再补充了
下一个开发计划
设计游戏玩法,加载游戏资源
PS:2023年6月7日补充:
新ecs架构调整后,大部分都进行了优化
1、去除了初始化系统,系统类全部采用静态方法
/**
* 总入口
*/
@ccclass('Main')
export class Main extends Root {
start() {
if (DEBUG) profiler.showStats(); // profiler.hideStats();
}
protected run() {
window['ooxh'] = ooxh // 方便console中查看全局
Logger.logBusiness('开始')
// 创建一个player实体
const player_entity = Entity.createEntity();
ooxh.game.playerEntity = player_entity
player_entity.attachComponent(PlayerStateComp) // 状态组件
player_entity.attachComponent(PlayerLoginComp) // 登录组件
player_entity.attachComponent(PlayerTouchMoveComp) // 触控组件
ooxh.gui.attachUI(UIID.Gate_Index)
}
}
/**
* 玩家登录系统及组件
*/
export class PlayerLoginSystem extends System {
static autoLogin(entity: Entity, callback: Function) {
const comp = entity.getComponent(PlayerLoginComp);
const playerStateComp = entity.getComponent(PlayerStateComp);
if (comp) {
var that = this
if (WECHAT) {
// 进行登录
wx.login({
success: function (res) {
that._apiGetOpenid(playerStateComp, Platform.Weixin, res.code, '', callback)
}
});
} else if (BYTEDANCE) {
// 进行登录
tt.login({
success: function (res) {
// 获取用户信息
tt.getUserInfo({
// withCredentials: true, // 是否需要返回敏感数据
withRealNameAuthenticationInfo: true, // 是否需要返回用户实名认证状态(真机情况下有效)
success(infoRes) {
if (infoRes.realNameAuthenticationStatus && infoRes.realNameAuthenticationStatus == 'certified') {
that._apiGetOpenid(playerStateComp, Platform.Douyin, res.code, '', callback) //已实名认证
} else {
if (typeof infoRes.realNameAuthenticationStatus == 'undefined') {
that._apiGetOpenid(playerStateComp, Platform.Douyin, res.code, '', callback) //说明在本地调试机上
} else {
tt.authenticateRealName({
complete(_res) {
that._apiGetOpenid(playerStateComp, Platform.Douyin, res.code, '', callback) // 因为抖音平台规定,暂时不认证不影响继续游戏
}
});
}
}
},
fail(infoRes) {
that._apiGetOpenid(playerStateComp, Platform.Douyin, res.code, '', callback) // 因为抖音平台规定,暂时不认证不影响继续游戏
},
});
},
fail(res) {
// 匿名用户
that._apiGetOpenid(playerStateComp, Platform.Douyin, res.code, res.anonymousCode, callback)
}
});
} else {
// h5为本地测试用的
that._apiGetOpenid(playerStateComp, Platform.H5, 'test' + (new Date).getDate(), '', callback)
}
}
}
static _apiGetOpenid(playerStateComp, platform, code, anonymousCode, callback) {
HttpUtil.Get('/appletapi/jianchuqiao/loginByCode',
{
code: code,
platform: platform,
anonymousCode: anonymousCode
},
(res) => {
if (res.code == 200) {
playerStateComp.platform = Platform.Weixin
playerStateComp.token = res.data.token
playerStateComp.playerInfo = res.data.playerInfo
sys.localStorage.setItem('jcq-token', playerStateComp.token)
callback && callback(true)
} else {
callback && callback(false)
}
})
}
}
export class PlayerLoginComp extends Comp {
callback: Function = null
reset() {
this.callback = null
}
onAttach(entity: Entity) {
PlayerLoginSystem.autoLogin(entity, (isOk) => {
if (isOk) {
entity.detachComponent(PlayerLoginComp)
this.callback && this.callback(this)
} else {
console.error('登录失败')
}
})
}
onDetach(entity) {
}
}