diff --git a/.creator/default-meta.json b/.creator/default-meta.json new file mode 100644 index 0000000..abb1239 --- /dev/null +++ b/.creator/default-meta.json @@ -0,0 +1,5 @@ +{ + "image": { + "type": "sprite-frame" + } +} diff --git a/.gitignore b/.gitignore index a19f004..0197d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,25 @@ -# ---> Vue -# gitignore template for Vue.js projects -# -# Recommended template: Node.gitignore -# TODO: where does this rule come from? -docs/_book +#/////////////////////////// +# Cocos Creator 3D Project +#/////////////////////////// +library/ +temp/ +local/ +build/ +profiles/ +native +*.meta +#////////////////////////// +# NPM +#////////////////////////// +node_modules/ -# TODO: where does this rule come from? -test/ +#////////////////////////// +# VSCode +#////////////////////////// +.vscode/ +#////////////////////////// +# WebStorm +#////////////////////////// +.idea/ \ No newline at end of file diff --git a/assets/scripts/gamePanel.ts b/assets/scripts/gamePanel.ts new file mode 100644 index 0000000..62490a4 --- /dev/null +++ b/assets/scripts/gamePanel.ts @@ -0,0 +1,55 @@ +import { _decorator, Button, Component, EditBox, Label } from 'cc' +import { WsClient } from './wsclient' +const { ccclass, property } = _decorator + +@ccclass('gamePanel') +export class gamePanel extends Component { + @property(EditBox) + private messageInput: EditBox = null + @property(Label) + private messageLabel: Label = null + @property(Button) + private sendButton: Button = null + + private onSendButtonClick() { + const msg = this.messageInput.string.trim() + if (!msg) return + const ws = WsClient.Instance + const chatMsg = { + type: 'chat', + content: msg, + } + ws.send(chatMsg) + this.messageInput.string = '' + this.appendMsg('你:' + msg) + } + + private onMessage(event: CustomEvent) { + try { + const msg = JSON.parse(event.detail) + if (msg.type === 'chat') { + this.appendMsg(`${msg.sender}:${msg.content}`) + } + } catch (error) { + console.log('收到非json消息:', event.detail) + } + } + private appendMsg(msg: string) { + this.messageLabel.string += msg + '\n' + } + + start() { + this.sendButton.node.on( + Button.EventType.CLICK, + this.onSendButtonClick, + this + ) + window.addEventListener(`ws-message`, this.onMessage.bind(this)) + } + + protected onDestroy(): void { + window.removeEventListener(`ws-message`, this.onMessage.bind(this)) + } + + update(deltaTime: number) {} +} diff --git a/assets/scripts/loginPanel.ts b/assets/scripts/loginPanel.ts new file mode 100644 index 0000000..e211a15 --- /dev/null +++ b/assets/scripts/loginPanel.ts @@ -0,0 +1,63 @@ +import { _decorator, Button, Component, EditBox, Label, Node } from 'cc' +import { WsClient } from './wsclient' +const { ccclass, property } = _decorator + +@ccclass('loginPanel') +export class loginPanel extends Component { + @property(EditBox) + private usernameInput: EditBox = null // 用户名输入框 + @property(Label) + private statusLable: Label = null // 状态提示标签 + @property(Button) + private loginButton: Button = null // 登录按钮 + + // 组件初始化完成后调用 + start() { + this.loginButton.node.on( + Button.EventType.CLICK, + this.onLoginButtonClick, + this + ) + // 监听websocket消息事件 + window.addEventListener('ws-message', this.onMessage.bind(this)) + } + + protected onDestroy(): void { + window.removeEventListener('ws-message', this.onMessage.bind(this)) + } + + // 登录按钮点击处理函数 + private onLoginButtonClick() { + const username = this.usernameInput.string.trim() + if (!username) { + this.statusLable.string = '请输入用户名' + return + } + const ws = WsClient.Instance + ws.connect('ws://echo.websocket.org') + this.statusLable.string = '正在连接服务器...' + setTimeout(() => { + const loginMsg = { + type: 'login', + username: username, + } + ws.send(loginMsg) + }, 1000) + } + + private onMessage(event: CustomEvent) { + try { + const message = JSON.parse(event.detail) // 解析消息内容 + if (message.type === 'loginSuccess') { + this.statusLable.string = '登录成功' + this.node.active = false + } else if (message.type === 'loginError') { + this.statusLable.string = '登录失败:' + message.reason + } + } catch (error) { + console.log('收到非json消息:', event.detail) + } + } + + update(deltaTime: number) {} +} diff --git a/assets/scripts/wsclient.ts b/assets/scripts/wsclient.ts new file mode 100644 index 0000000..e95721a --- /dev/null +++ b/assets/scripts/wsclient.ts @@ -0,0 +1,140 @@ +import { _decorator, Component, Node } from 'cc' +const { ccclass, property } = _decorator + +@ccclass('WsClient') +export class WsClient extends Component { + private socket: WebSocket | null = null + private url: string = 'ws://echo.websocket.org' // 测试服务器 + private reconnectTimerId: number = 0 + private maxReconnectCount: number = 5 + private reconnectCount: number = 0 + private isConnected: boolean = false + + private static instance: WsClient = null + + // 单例 + public static get Instance(): WsClient { + return this.instance + } + + public get IsConnected(): boolean { + return this.isConnected + } + + // 组件加载时自动调用 + protected onLoad(): void { + if (WsClient.instance) { + this.destroy() + return + } + WsClient.instance = this + this.node.name = 'WsClient' + this.node.addComponent(WsClient) + } + + // 关闭WebSocket连接 + close() { + if (this.reconnectTimerId > 0) { + clearTimeout(this.reconnectTimerId) + this.reconnectTimerId = 0 + } + if (this.socket) { + // 清空所有事件处理函数 + this.socket.onopen = null + this.socket.onmessage = null + this.socket.onclose = null + this.socket.onerror = null + this.socket.close() + this.socket = null + } + this.isConnected = false + } + + // 连接websocket服务器 + connect(url?: string) { + if (url) this.url = url + if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { + // 如果已有连接且未关闭 + this.close() + } + try { + this.socket = new WebSocket(this.url) + this.socket.onopen = this.onOpen.bind(this) + this.socket.onmessage = this.onMessage.bind(this) + this.socket.onclose = this.onClose.bind(this) + this.socket.onerror = this.onError.bind(this) + this.reconnectCount = 0 + this.isConnected = true + console.log(`正在连接websocket服务器:${this.url}`) + } catch (error) { + console.error('创建websocket失败:', error) + this.scheduleReconnect() + } + } + + // 发送消息到服务器 + send(message: string | object) { + if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { + // 如果连接不存在或未打开 + console.error('websocket未连接,无法发送消息') + return false + } + var msg: string = '' + if (typeof message == 'string') { + msg = message + } else { + msg = JSON.stringify(message) + } + this.socket.send(msg) + return true + } + + // 连接成功回调 + private onOpen() { + console.log('websocket连接已建立') + this.reconnectCount = 0 + } + + // 收到消息回调 + private onMessage(event: MessageEvent) { + console.log('收到消息:', event.data) + window.dispatchEvent(new CustomEvent('ws-message', { detail: event.data })) + } + + // 连接关闭回调 + private onClose(event: CloseEvent) { + console.log('websocket连接已关闭,代码:', event.code, '原因:', event.reason) + this.socket = null + this.scheduleReconnect() + } + + // 连接错误回调 + private onError(error: Event) { + console.error('websocket发生错误:', error) + // 不需要在这里重连,onClose里会重连 + } + + // 重连 + private scheduleReconnect() { + if (this.reconnectCount >= this.maxReconnectCount) { + console.error(`达到最大重连次数:${this.maxReconnectCount},停止重连`) + return + } + + this.reconnectCount++ + const delay = 2000 + console.log( + `尝试重连(${this.reconnectCount}/${this.maxReconnectCount}),${ + delay / 1000 + }秒后...` + ) + this.reconnectTimerId = setTimeout(() => { + this.connect() + }, delay) + } + + // 组件销毁时自动调用 + protected onDestroy(): void { + this.close() + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b664c8f --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "gameClient", + "uuid": "76b48d58-b2ad-4a8f-9f01-f49e3d3f5d64", + "creator": { + "version": "3.8.6" + } +} diff --git a/settings/v2/packages/builder.json b/settings/v2/packages/builder.json new file mode 100644 index 0000000..7526e40 --- /dev/null +++ b/settings/v2/packages/builder.json @@ -0,0 +1,3 @@ +{ + "__version__": "1.3.9" +} diff --git a/settings/v2/packages/cocos-service.json b/settings/v2/packages/cocos-service.json new file mode 100644 index 0000000..0a7466a --- /dev/null +++ b/settings/v2/packages/cocos-service.json @@ -0,0 +1,23 @@ +{ + "__version__": "3.0.9", + "game": { + "name": "未知游戏", + "app_id": "UNKNOW", + "c_id": "0" + }, + "appConfigMaps": [ + { + "app_id": "UNKNOW", + "config_id": "d45dc9" + } + ], + "configs": [ + { + "app_id": "UNKNOW", + "config_id": "d45dc9", + "config_name": "Default", + "config_remarks": "", + "services": [] + } + ] +} diff --git a/settings/v2/packages/device.json b/settings/v2/packages/device.json new file mode 100644 index 0000000..70e599e --- /dev/null +++ b/settings/v2/packages/device.json @@ -0,0 +1,3 @@ +{ + "__version__": "1.0.1" +} diff --git a/settings/v2/packages/engine.json b/settings/v2/packages/engine.json new file mode 100644 index 0000000..cb7acb8 --- /dev/null +++ b/settings/v2/packages/engine.json @@ -0,0 +1,223 @@ +{ + "__version__": "1.0.12", + "modules": { + "configs": { + "defaultConfig": { + "name": "Default Config", + "cache": { + "base": { + "_value": true + }, + "gfx-webgl": { + "_value": true + }, + "gfx-webgl2": { + "_value": true + }, + "gfx-webgpu": { + "_value": false + }, + "animation": { + "_value": true + }, + "skeletal-animation": { + "_value": false + }, + "3d": { + "_value": false + }, + "meshopt": { + "_value": false + }, + "2d": { + "_value": true + }, + "xr": { + "_value": false + }, + "rich-text": { + "_value": true + }, + "mask": { + "_value": true + }, + "graphics": { + "_value": true + }, + "ui-skew": { + "_value": false + }, + "affine-transform": { + "_value": true + }, + "ui": { + "_value": true + }, + "particle": { + "_value": false + }, + "physics": { + "_value": false, + "_option": "physics-ammo" + }, + "physics-ammo": { + "_value": false, + "_flags": { + "LOAD_BULLET_MANUALLY": false + } + }, + "physics-cannon": { + "_value": false + }, + "physics-physx": { + "_value": false, + "_flags": { + "LOAD_PHYSX_MANUALLY": false + } + }, + "physics-builtin": { + "_value": false + }, + "physics-2d": { + "_value": true, + "_option": "physics-2d-box2d" + }, + "physics-2d-box2d": { + "_value": false + }, + "physics-2d-box2d-wasm": { + "_value": false, + "_flags": { + "LOAD_BOX2D_MANUALLY": false + } + }, + "physics-2d-builtin": { + "_value": false + }, + "physics-2d-box2d-jsb": { + "_value": false + }, + "intersection-2d": { + "_value": true + }, + "primitive": { + "_value": false + }, + "profiler": { + "_value": true + }, + "occlusion-query": { + "_value": false + }, + "geometry-renderer": { + "_value": false + }, + "debug-renderer": { + "_value": false + }, + "particle-2d": { + "_value": true + }, + "audio": { + "_value": true + }, + "video": { + "_value": true + }, + "webview": { + "_value": true + }, + "tween": { + "_value": true + }, + "websocket": { + "_value": false + }, + "websocket-server": { + "_value": false + }, + "terrain": { + "_value": false + }, + "light-probe": { + "_value": false + }, + "tiled-map": { + "_value": true + }, + "vendor-google": { + "_value": false + }, + "spine": { + "_value": true, + "_option": "spine-3.8" + }, + "spine-3.8": { + "_value": true, + "_flags": { + "LOAD_SPINE_MANUALLY": false + } + }, + "spine-4.2": { + "_value": false, + "_flags": { + "LOAD_SPINE_MANUALLY": false + } + }, + "dragon-bones": { + "_value": true + }, + "marionette": { + "_value": false + }, + "procedural-animation": { + "_value": false + }, + "custom-pipeline-post-process": { + "_value": false + }, + "render-pipeline": { + "_value": true, + "_option": "custom-pipeline" + }, + "custom-pipeline": { + "_value": true + }, + "legacy-pipeline": { + "_value": false + } + }, + "includeModules": [ + "2d", + "affine-transform", + "animation", + "audio", + "base", + "custom-pipeline", + "dragon-bones", + "gfx-webgl", + "gfx-webgl2", + "graphics", + "intersection-2d", + "mask", + "particle-2d", + "physics-2d-box2d", + "profiler", + "rich-text", + "spine-3.8", + "tiled-map", + "tween", + "ui", + "video", + "webview" + ], + "noDeprecatedFeatures": { + "value": false, + "version": "" + }, + "flags": {} + } + }, + "globalConfigKey": "defaultConfig" + } +} diff --git a/settings/v2/packages/information.json b/settings/v2/packages/information.json new file mode 100644 index 0000000..a423151 --- /dev/null +++ b/settings/v2/packages/information.json @@ -0,0 +1,23 @@ +{ + "__version__": "1.0.1", + "information": { + "customSplash": { + "id": "customSplash", + "label": "customSplash", + "enable": true, + "customSplash": { + "complete": false, + "form": "https://creator-api.cocos.com/api/form/show?sid=3a2396ab7a0af5fe87295c6e46435e85" + } + }, + "removeSplash": { + "id": "removeSplash", + "label": "removeSplash", + "enable": true, + "removeSplash": { + "complete": false, + "form": "https://creator-api.cocos.com/api/form/show?sid=3a2396ab7a0af5fe87295c6e46435e85" + } + } + } +} diff --git a/settings/v2/packages/program.json b/settings/v2/packages/program.json new file mode 100644 index 0000000..916c1b2 --- /dev/null +++ b/settings/v2/packages/program.json @@ -0,0 +1,3 @@ +{ + "__version__": "1.0.4" +} diff --git a/settings/v2/packages/project.json b/settings/v2/packages/project.json new file mode 100644 index 0000000..4129dde --- /dev/null +++ b/settings/v2/packages/project.json @@ -0,0 +1,3 @@ +{ + "__version__": "1.0.6" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7dc649a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + /* Base configuration. Do not edit this field. */ + "extends": "./temp/tsconfig.cocos.json", + + /* Add your custom configuration here. */ + "compilerOptions": { + "strict": false + } +}