下载Cocos Dashboard
创建按一个3D项目
场景里添加2个UI对象,按钮,编辑框
左下添加一个TS脚本NewComponent添加到Canvas上作为按钮的组件
TS里写
onBtnClick(event, customEventData){
var editBox = this.node.getChildByName("EditBox").getComponent(EditBox);
console.log('按钮按下'+editBox.textLabel.string);
}
然后按钮事件挂自己的按钮节点,选择这个脚本的这个方法。
点上面三角形运行后会直接打开Edge浏览器,F12打开浏览器调试分割窗口,点击按钮看到右边出现“按钮按下”文字
场景名字改为login
把官方例子的Js代码改为Ts代码调用注册:
import { _decorator, Component, EditBox, EventMouse,PhysicsSystem,Node,resources,Prefab,instantiate, Vec3 ,geometry, Camera,Input,input, CameraComponent} from 'cc';
import { encode,decode } from "./msgpack-ts-master/src"
const { ccclass, property } = _decorator;
const _RECV_PAYLOAD_LENGTH = 1
const _RECV_PAYLOAD = 2
const CLIENTID_LENGTH = 16
const ENTITYID_LENGTH = 16
const SIZE_FIELD_SIZE = 4
const MT_INVALID = 0
// Server Messages
const MT_SET_GAME_ID = 1
const MT_SET_GATE_ID = 2
const MT_NOTIFY_CREATE_ENTITY = 3
const MT_NOTIFY_DESTROY_ENTITY = 4
const MT_DECLARE_SERVICE = 5
const MT_UNDECLARE_SERVICE = 6
const MT_CALL_ENTITY_METHOD = 7
const MT_CREATE_ENTITY_ANYWHERE = 8
const MT_LOAD_ENTITY_ANYWHERE = 9
const MT_NOTIFY_CLIENT_CONNECTED = 10
const MT_NOTIFY_CLIENT_DISCONNECTED = 11
const MT_CALL_ENTITY_METHOD_FROM_CLIENT = 12
const MT_SYNC_POSITION_YAW_FROM_CLIENT = 13
const MT_NOTIFY_ALL_GAMES_CONNECTED = 14
const MT_NOTIFY_GATE_DISCONNECTED = 15
const MT_START_FREEZE_GAME = 16
const MT_START_FREEZE_GAME_ACK = 17
// Message types for migrating
const MT_MIGRATE_REQUEST = 18
const MT_REAL_MIGRATE = 19
const MT_GATE_SERVICE_MSG_TYPE_START = 1000
const MT_REDIRECT_TO_GATEPROXY_MSG_TYPE_START = 1001 // messages that should be redirected to client proxy
const MT_CREATE_ENTITY_ON_CLIENT = 1002
const MT_DESTROY_ENTITY_ON_CLIENT = 1003
const MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT = 1004
const MT_NOTIFY_MAP_ATTR_DEL_ON_CLIENT = 1005
const MT_NOTIFY_LIST_ATTR_CHANGE_ON_CLIENT = 1006
const MT_NOTIFY_LIST_ATTR_POP_ON_CLIENT = 1007
const MT_NOTIFY_LIST_ATTR_APPEND_ON_CLIENT = 1008
const MT_CALL_ENTITY_METHOD_ON_CLIENT = 1009
const MT_UPDATE_POSITION_ON_CLIENT = 1010
const MT_UPDATE_YAW_ON_CLIENT = 1011
const MT_SET_CLIENTPROXY_FILTER_PROP = 1012
const MT_CLEAR_CLIENTPROXY_FILTER_PROPS = 1013
// add more ...
const MT_REDIRECT_TO_GATEPROXY_MSG_TYPE_STOP = 1500
const MT_CALL_FILTERED_CLIENTS = 1501
const MT_SYNC_POSITION_YAW_ON_CLIENTS = 1502
// add more ...
const MT_GATE_SERVICE_MSG_TYPE_STOP = 2000
export class ClientEntity{
ID:string;
isPlayer=false;
owner:NewComponent=null;
typeName:string=null;
attrs:{ [key: string]: any; } ={};
main:NewComponent;
create(owner:NewComponent, typeName:string, entityID:string, attrs:Uint8Array) {
this.owner = owner
this.typeName = typeName
this.ID = entityID
this.attrs = attrs
this.isPlayer = false
}
onCreated() {
if (this.typeName == "Account") {
console.log("Account created, start logining...")
}
}
onBecomePlayer() {
let scene = cc.director.getScene()
console.log("获得玩家对象:", this, scene.name)
if (this.typeName == "Avatar") {
// 玩家登录成功!
this.getGoWorld().onAvatarLoginSuccess( this )
} else if (this.typeName == "Account") {
// 账号创建成功,可以开始登陆
if (scene.name != "login") {
cc.director.loadScene("login");
}
}
}
onCall(method, ...args:Uint8Array[]) {
console.log(this+",onCall,"+method+"("+args+")")
// this[method](...args)
this[method](args)//20230929
}
ShowInfo(msg){
console.info(msg)
}
// Put Client Methods Here
ShowError(msg:string) {
// this.getGoWorld().showErrorTip(msg)
console.error('ShowError'+msg);
}
DisplayAttack(playerID:string):void {
// ClientEntity player = GoWorld.GetEntity (playerID);
let player = this.main.entities[playerID];
console.warn (this + " attack " + playerID + " " + player);
// Vector3 startPos = this.gameObject.transform.position;
// Vector3 endPos = player.gameObject.transform.position;
// startPos.y = 0.5f;
// endPos.y = 0.5f;
// lineRenderer.SetPosition(0, startPos);
// lineRenderer.SetPosition (1, endPos);
// lineRenderer.enabled = true;
// attackTime = Time.time;
}
applyMapAttrChange(path:{[key:string]:any}, key:string, val:any) {
let attr = this.getAttrByPath(path)
var rootkey = path!=null&&path.length > 0 ? path[0] : key
attr[key] = val
var methodName = 'onAttrChange_'+rootkey;
console.log('methodName=',methodName);
this[methodName]();
}
getAttrByPath(path:any): any {
var attr = this.attrs
if(path==null)
return attr;
for (var i=0; i< path.length;i++) {
let key = path[i]
attr = attr[key]
}
return attr
}
onAttrChange_action(){
console.log('onAttrChange_action,'+this.attrs['action']);
}
onAttrChange_hp(){
console.log('onAttrChange_hp,'+this.attrs['hp']);
}
onAttrChange_name(){
console.log('onAttrChange_name,'+this.attrs['name']);
}
onAttrChange_lv(){
console.log('onAttrChange_lv,'+this.attrs['lv']);
}
onAttrChange_hpmax(){
console.log('onAttrChange_hpmax,'+this.attrs['hpmax']);
}
}
@ccclass('NewComponent')
export class NewComponent extends Component {
ID="ID_TEST";
// entities:Record<string, string>;
recvBuf = new ArrayBuffer(0);
recvStatus = _RECV_PAYLOAD_LENGTH;
recvPayloadLen = 0;
entities : { [key: string]: ClientEntity; } ={};
sendBuf = new ArrayBuffer(1024*1024);
_sendPacket = new DataView(this.sendBuf);
_sendPacketWritePos = SIZE_FIELD_SIZE;
player:ClientEntity=null;
editBox:EditBox=null;
plane:Node;
camera:CameraComponent
start() {
let main = this;
this.plane=this.node.parent.getChildByName("Plane");
this.camera=this.node.parent.getChildByName("Main Camera").getComponent(CameraComponent);
input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
resources.load("Cube", Prefab, (err, prefab) => {
console.log('resources.load callback',err,prefab);
const newNode = instantiate(prefab);
this.plane.addChild(newNode);
newNode.position =new Vec3(-3,0,0);
console.log('resources.load newNode',newNode);
});
// this.recvBuf = new ArrayBuffer()
// this.recvStatus = _RECV_PAYLOAD_LENGTH
// this.recvPayloadLen = 0
// this.entities = {}
// this.sendBuf = new ArrayBuffer(1024*1024);
// this._sendPacket = new DataView(this.sendBuf);
// this._sendPacketWritePos = SIZE_FIELD_SIZE
this.editBox = this.node.getChildByName("EditBox").getComponent(EditBox);
// this.editBox.string = 'asdf';
this.connect();
}
update(deltaTime: number) {
}
onMouseUp(eventMouse: EventMouse) {
// if (event.getButton() === 0) {
// this.jumpByStep(1);
// } else if (event.getButton() === 2) {
// this.jumpByStep(2);
// }
// }
let ray = new geometry.Ray();
this.camera.screenPointToRay(eventMouse.getLocationX(), eventMouse.getLocationY(), ray);
// 以下参数可选
const mask = 0xffffffff;
const maxDistance = 10000000;
const queryTrigger = true;
console.log( eventMouse );
console.log( ray );
if (PhysicsSystem.instance.raycastClosest(ray)){//}, mask, maxDistance, queryTrigger)) {
const raycastClosestResult = PhysicsSystem.instance.raycastClosestResult;
const hitPoint = raycastClosestResult.hitPoint
const hitNormal = raycastClosestResult.hitNormal;
const collider = raycastClosestResult.collider;
const distance = raycastClosestResult.distance;
console.log( collider.node.name , hitPoint );
}
}
onBtnClick(event, customEventData){
// var editBox = this.node.getChildByName("EditBox").getComponent(EditBox);
console.log('按钮按下,edit='+this.editBox.string);
let account = this.getEntityByType("Account")
this.callServer(account,"Register",this.editBox.string,"asdf");
}
onLogin() {
// let loginUser = cc.find("loginUser").getComponent("cc.EditBox").string
// let loginPwd = cc.find("loginPwd").getComponent("cc.EditBox").string
console.log("登录...", this.editBox.string, "asdf" )
let account = this.getEntityByType("Account")
console.log("account", account !== null ? account.toString():null)
if (account === null) {
console.error("正在连接服务器,请耐心等待")
return
}
// if (loginUser == "" || loginPwd == "") {
// console.error("请输入用户名和密码!")
// return
// }
this.callServer(account,"Login", this.editBox.string, "asdf")
}
getEntityByType(typeName) {
for (var eid in this.entities) {
let e = this.entities[eid]
if (e.typeName == typeName) {
return e
}
}
return null
}
callServer(entity:ClientEntity, method , ...args2) {
var args = Array.prototype.slice.call(arguments);
args = args.slice(2)
this.callServerMethod( entity, method, args )
}
callServerMethod(entity:ClientEntity, method:string, args) {
console.log(">>> "+entity+"."+method+"("+args+")")
// packet.AppendUint16(MT_CALL_ENTITY_METHOD_FROM_CLIENT)
// packet.AppendEntityID(id)
// packet.AppendVarStr(method)
// packet.AppendArgs(args)
this.appendUint16(MT_CALL_ENTITY_METHOD_FROM_CLIENT)
this.appendEntityID(entity.ID)
this.appendVarStr(method)
this.appendArgs(args)
this.sendPacket()
}
appendUint16(v) {
this._sendPacket.setUint16(this._sendPacketWritePos, v, true)
this._sendPacketWritePos += 2
}
appendEntityID(eid) {
console.log(eid);
let b = this.string2Uint8Array(eid)
console.log("convert", eid, "to", b, b.length)
this.appendBytes(b)
}
string2Uint8Array(str) {
console.log(str);
let bufView = new Uint8Array(str.length);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return bufView;
}
appendBytes(b) {
new Uint8Array(this._sendPacket.buffer, this._sendPacketWritePos, b.length).set(b, 0);
this._sendPacketWritePos += b.length
}
appendVarStr(s) {
let b = this.string2Uint8Array(s)
this.appendVarBytes(b)
}
appendVarBytes(b) {
this.appendUint32(b.length)
this.appendBytes(b)
}
appendUint32(v) {
this._sendPacket.setUint32(this._sendPacketWritePos, v, true)
this._sendPacketWritePos += 4
}
appendArgs(args) {
console.log("appendArgs", args.length, args)
this.appendUint16(args.length)
for (var i=0; i<args.length;i++) {
this.appendData(args[i])
}
}
appendData(data) {
data = encode(data)
console.log("msgpack encode:", typeof(data), data.length)
this.appendVarBytes(data)
}
sendPacket() {
let payloadLen = this._sendPacketWritePos - SIZE_FIELD_SIZE
this._sendPacket.setUint32(0, payloadLen, true)
let packetLen = this._sendPacketWritePos
this._sendPacketWritePos = SIZE_FIELD_SIZE
console.log("sendPacket:", packetLen)
this.websocket.send(this.sendBuf.slice(0, packetLen))
}
websocket : WebSocket = null;
connect() {
var serverAddr='localhost';
var serverPort=15101;
var serverAddr = 'ws://'+serverAddr+':'+serverPort+'/ws'
console.log("正在连接 " + serverAddr + ' ...')
var websocket = new WebSocket(serverAddr)
this.websocket = websocket
websocket.binaryType = 'arraybuffer'
console.log(websocket)
var gameclient = this
//连接发生错误的回调方法
websocket.onerror = function () {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
var data = event.data
console.log("收到数据:", typeof(data), data.length);
gameclient.onRecvData(data)
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
console.log("onbeforeunload");
}
}
onRecvData(data) {
if (this.recvBuf.byteLength == 0) {
this.recvBuf = data
} else {
var tmp = new Uint8Array( this.recvBuf.byteLength + data.byteLength );
tmp.set( new Uint8Array( this.recvBuf ), 0 );
tmp.set( new Uint8Array( data ), this.recvBuf.byteLength );
this.recvBuf = tmp.buffer
}
console.log("未处理数据:", this.recvBuf.byteLength)
while (true) {
let payload = this.tryReceivePacket()
if (payload !== null) {
this.onReceivePacket(payload)
} else {
break
}
}
}
// 从已经收到的数据(recvBuf)里解析出数据包(Packet)
tryReceivePacket() {
let recvBufView = new DataView(this.recvBuf)
if (this.recvStatus == _RECV_PAYLOAD_LENGTH) {
if (this.recvBuf.byteLength < SIZE_FIELD_SIZE) {
return null
}
this.recvPayloadLen = recvBufView.getUint32(0, true)
console.log("数据包大小: ", this.recvPayloadLen)
this.recvStatus = _RECV_PAYLOAD
}
// recv status == _RECV_PAYLOAD
console.log("包大小:", this.recvPayloadLen, "现有数据:", this.recvBuf.byteLength - SIZE_FIELD_SIZE)
if (this.recvBuf.byteLength - SIZE_FIELD_SIZE < this.recvPayloadLen) {
// payload not enough
return null
}
// 足够了,返回包数据
var payload = this.recvBuf.slice(SIZE_FIELD_SIZE, SIZE_FIELD_SIZE+this.recvPayloadLen)
this.recvBuf = this.recvBuf.slice(SIZE_FIELD_SIZE+this.recvPayloadLen)
// 恢复到接收长度状态
this.recvStatus = _RECV_PAYLOAD_LENGTH
this.recvPayloadLen = 0
return payload
}
onReceivePacket (payload2) {
// payload is ArrayBuffer
payload = new DataView(payload2) // 转换为DataView便于操作
var [msgtype, payload] = this.readUint16(payload)
console.log("收到包:", payload, payload.byteLength, ",消息类型:", msgtype)
if (msgtype != MT_CALL_FILTERED_CLIENTS && msgtype != MT_SYNC_POSITION_YAW_ON_CLIENTS) {
var [dummy, payload] = this.readUint16(payload)
console.log("gateid", dummy)
var [dummy2, payload] = this.readBytes(payload, CLIENTID_LENGTH) // read ClientID
console.log("clientid", dummy2.length)
}
if (msgtype == MT_CREATE_ENTITY_ON_CLIENT) {
this.handleCreateEntityOnClient(payload)
} else if (msgtype == MT_CALL_ENTITY_METHOD_ON_CLIENT) {
this.handleCallEntityMethodOnClient(payload)
} else if (msgtype == MT_DESTROY_ENTITY_ON_CLIENT) {
this.handleDestroyEntityOnClient(payload)
} else if (msgtype == MT_CALL_FILTERED_CLIENTS) {
this.handleCallFilteredClients(payload)
} else if (msgtype == MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT) {
this.handleNotifyMapAttrChangeOnClient(payload)
} else if (msgtype == MT_NOTIFY_MAP_ATTR_DEL_ON_CLIENT) {
} else if (msgtype == MT_NOTIFY_LIST_ATTR_APPEND_ON_CLIENT) {
} else if (msgtype == MT_NOTIFY_LIST_ATTR_CHANGE_ON_CLIENT) {
} else if (msgtype == MT_NOTIFY_LIST_ATTR_POP_ON_CLIENT) {
} else if (msgtype == MT_SYNC_POSITION_YAW_ON_CLIENTS) {
this.handleSyncPositionYawOnClients(payload)
} else {
console.error("无法识别的消息类型:"+msgtype)
}
}
handleSyncPositionYawOnClients(payload:DataView):void
{
while(0 < payload.byteLength)// while (pkt.UnreadPayloadLen > 0)
{
console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
var [entityID, payload] = this.readEntityID(payload)// string entityID = pkt.ReadEntityID();
// float x = pkt.ReadFloat32();
// float y = pkt.ReadFloat32();
// float z = pkt.ReadFloat32();
// float yaw = pkt.ReadFloat32();
var [x, payload] = this.readFloat32(payload)
console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
var [y, payload] = this.readFloat32(payload)
console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
var [z, payload] = this.readFloat32(payload)
console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
var [yaw, payload] = this.readFloat32(payload)
console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
// EntityManager.Instance.OnSyncEntityInfo(entityID, x, y, z, yaw);
console.log('handleSyncPositionYawOnClients',entityID,x,y,z,yaw)
}
}
handleNotifyMapAttrChangeOnClient(payload:DataView) {
var [entityID, payload] = this.readEntityID(payload)
var [path, payload] = this.readData(payload)
var [key, payload] = this.readVarStr(payload)
var [val, payload] = this.readData(payload)
console.log("MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT", entityID, "path", typeof(path), JSON.stringify(path), "key", key, "val", val)
// console.log("MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT", entityID, "path", typeof(path), JSON.stringify(path), path.length, "key", key, "val", val)
let e = this.entities[entityID]
if (!e) {
console.log("找不到对象:"+entityID)
return
}
e.applyMapAttrChange( path, key, val )
}
readUint8(buf:DataView):[any,DataView] {
let v = buf.getUint8(0)
return [v, new DataView(buf.buffer, buf.byteOffset+1)]
}
readUint16(buf:DataView):[any,DataView] {
let v = buf.getUint16(0, true)
return [v, new DataView(buf.buffer, buf.byteOffset+2)]
}
readUint32(buf:DataView) :[any,DataView]{
let v = buf.getUint32(0, true)
return [v, new DataView(buf.buffer, buf.byteOffset+4)]
}
readFloat32(buf:DataView) :[Number,DataView]{
let v = buf.getFloat32(0, true)
return [v, new DataView(buf.buffer, buf.byteOffset+4)]
}
readBytes(buf:DataView, length) :[Uint8Array,DataView]{
let v = new Uint8Array(buf.buffer, buf.byteOffset, length)
return [v, new DataView(buf.buffer, buf.byteOffset+length)]
}
readVarBytes(buf:DataView) :[Uint8Array,DataView]{
var [n, buf] = this.readUint32(buf)
var [b, buf] = this.readBytes(buf, n)
console.log('VarBytes len', n, 'b', b.length)
return [b, buf]
}
readEntityID(buf:DataView) :[string,DataView]{
var [eid, buf] = this.readBytes(buf, ENTITYID_LENGTH)
var eid2 = this.uint8Array2String(eid)
return [eid2, buf]
}
readVarStr(buf:DataView) :[any,DataView]{
var [b, buf] = this.readVarBytes(buf)
let s = this.uint8Array2String(b)
return [s, buf]
}
readBool(buf):[any,DataView] {
var b
[b, buf] = this.readUint8(buf)
b = b == 0 ? false : true
return [b, buf]
}
readData(buf:DataView):[any,DataView] {
var [b, buf] = this.readVarBytes(buf)
let data = decode(b)//二进制转对象(带具体类型),反序列化,功能很强 20230929
return [data, buf]
// return [b, buf]
}
readArgs(buf:DataView) :[any[],DataView]{
var [argcount, buf] = this.readUint16(buf)
console.log("readArgs: argcount", argcount)
var args = new Array<any>(argcount)
for (var i = 0; i<argcount; i++) {
var [data, buf] = this.readData(buf)
args[i] = data
}
return [args, buf]
}
handleCreateEntityOnClient(payload:DataView) {
var [isPlayer, payload] = this.readBool(payload)
var [eid, payload] = this.readEntityID(payload)
var [typeName, payload] = this.readVarStr(payload)
var [x, payload] = this.readFloat32(payload)
var [y, payload] = this.readFloat32(payload)
var [z, payload] = this.readFloat32(payload)
var [yaw, payload] = this.readFloat32(payload)
var [clientData,payload] = this.readVarBytes(payload)
clientData = decode(clientData)
console.log("MT_CREATE_ENTITY_ON_CLIENT", "isPlayer", isPlayer, 'eid', eid,"typeName", typeName, 'position', x, y, z, 'yaw', yaw, 'clientData', JSON.stringify(clientData))
var e = new ClientEntity()
e.main=this;
e.create( this, typeName, eid, clientData )
this.entities[eid] = e
if (isPlayer) {
e.isPlayer = true
if (this.player) {
// dupliate player!!!
console.error("玩家对象重复:老玩家"+this.player.toString() + ",新玩家:", e.toString())
}
this.player = e
}
this.onEntityCreated(e)
e.onCreated()
if (this.player === e) {
e.onBecomePlayer()
}
}
onEntityCreated(e) {
console.log("entity created:", e.toString())
}
uint8Array2String(b) :string{
return String.fromCharCode.apply(null, b)
}
handleDestroyEntityOnClient(payload:DataView) {
// typeName := packet.ReadVarStr()
// entityID := packet.ReadEntityID()
var [typeName, payload] = this.readVarStr(payload)
var [entityID, payload] = this.readEntityID(payload)
let e = this.entities[entityID]
if (e == undefined) {
return
}
delete this.entities[entityID]
if (this.player === e) {
this.player = null
console.log("失去玩家对象:", e.toString())
}
}
handleCallEntityMethodOnClient(payload:DataView) {
// entityID := packet.ReadEntityID()
// method := packet.ReadVarStr()
// args := packet.ReadArgs()
var [entityID, payload] = this.readEntityID(payload)
var [method, payload] = this.readVarStr(payload)
var [args, payload] = this.readArgs(payload)
console.log("MT_CALL_ENTITY_METHOD_ON_CLIENT", "entityID", entityID, "method", method, "args", args.length, args)
let e = this.entities[entityID]
if (e == undefined) {
console.log("找不到entity:", entityID)
return
}
e.onCall( method, ...args )
}
handleCallFilteredClients(payload:DataView) {
var [fkey, payload] = this.readVarStr(payload)
var [fval, payload] = this.readVarStr(payload)
var [method, payload] = this.readVarStr(payload)
var [args, payload] = this.readArgs(payload)
console.log("MT_CALL_FILTERED_CLIENTS", fkey, "=", fval, "method=", method, "args=", args)
this.player.onCall( method, ...args )
}
}
raycastClosest要加了BoxCollider才能生效
完整Cocos Creator 3.8项目同步到:
git@gitee.com:griffon2/go-world-unit-demo-cocos-creator38.git