cocos multiplayer

This commit is contained in:
k8w 2021-12-02 00:38:28 +08:00
parent 3b4fe77647
commit 49f8c94974
68 changed files with 3459 additions and 1398 deletions

View File

@ -1,3 +1,4 @@
node_modules node_modules
dist dist
.DS_STORE .DS_STORE
*.meta

View File

@ -22,6 +22,6 @@
"typescript": "^4.5.2" "typescript": "^4.5.2"
}, },
"dependencies": { "dependencies": {
"tsrpc": "^3.1.2" "tsrpc": "^3.1.3-dev.1"
} }
} }

View File

@ -0,0 +1,12 @@
import { ApiCallWs } from "tsrpc";
import { roomInstance } from "..";
import { ReqJoinRoom, ResJoinRoom } from "../shared/protocols/PtlJoinRoom";
export async function ApiJoinRoom(call: ApiCallWs<ReqJoinRoom, ResJoinRoom>) {
let uid = roomInstance.join(call.req, call.conn);
call.succ({
uid: uid,
roomState: roomInstance.state
})
}

View File

@ -1,26 +0,0 @@
import { ApiCall } from "tsrpc";
import { server } from "..";
import { ReqSend, ResSend } from "../shared/protocols/PtlSend";
// This is a demo code file
// Feel free to delete it
export async function ApiSend(call: ApiCall<ReqSend, ResSend>) {
// Error
if (call.req.content.length === 0) {
call.error('Content is empty')
return;
}
// Success
let time = new Date();
call.succ({
time: time
});
// Broadcast
server.broadcastMsg('Chat', {
content: call.req.content,
time: time
})
}

View File

@ -1,5 +1,7 @@
import 'k8w-extend-native';
import * as path from "path"; import * as path from "path";
import { WsServer } from "tsrpc"; import { WsServer } from "tsrpc";
import { Room } from './models/Room';
import { serviceProto } from './shared/protocols/serviceProto'; import { serviceProto } from './shared/protocols/serviceProto';
// Create the Server // Create the Server
@ -9,6 +11,9 @@ export const server = new WsServer(serviceProto, {
json: true json: true
}); });
// 测试,只有一个房间
export const roomInstance = new Room(server);
// Initialize before server start // Initialize before server start
async function init() { async function init() {
await server.autoImplementApi(path.resolve(__dirname, 'api')); await server.autoImplementApi(path.resolve(__dirname, 'api'));

View File

@ -0,0 +1,102 @@
import { WsConnection, WsServer } from "tsrpc";
import { ReqJoinRoom } from "../shared/protocols/PtlJoinRoom";
import { MsgFrame } from "../shared/protocols/serverMsgs/MsgFrame";
import { ServiceType } from "../shared/protocols/serviceProto";
import { applyPlayerInput, PlayerInput, PlayerState } from "../shared/states/Player";
import { RoomState } from "../shared/states/RoomState";
/**
* - -
*/
export class Room {
state: RoomState = {
players: []
}
// 次数/秒
syncRate = 10;
lastUid = 0;
server: WsServer<ServiceType>;
conns: WsConnection[] = [];
pendingInputs: MsgFrame['inputs'] = [];
constructor(server: WsServer<ServiceType>) {
this.server = server;
setInterval(() => { this.sendSyncFrame() }, 1000 / this.syncRate);
}
sendSyncFrame() {
// 发送同步帧
this.server.broadcastMsg('serverMsgs/Frame', {
inputs: this.pendingInputs
}, this.conns);
this.pendingInputs = [];
}
/** 加入房间 */
join(req: ReqJoinRoom, conn: WsConnection<ServiceType>) {
let player: PlayerState = {
...req,
uid: ++this.lastUid,
// 初始位置随机
pos: {
x: Math.random() * 10,
y: Math.random() * 10
}
}
this.conns.push(conn);
this.state.players.push(player);
conn.uid = player.uid;
conn.listenMsg('clientMsgs/Input', call => {
this.pendingInputs.push({
uid: player.uid,
msgInput: call.msg
});
this.applyInput({
uid: player.uid,
input: call.msg
});
});
this.server.broadcastMsg('serverMsgs/Join', {
player: player
}, this.conns);
return player.uid;
}
/** 离开房间 */
leave(uid: number, conn: WsConnection<ServiceType>) {
this.conns.removeOne(v => v.uid === uid);
this.state.players.removeOne(v => v.uid === uid);
conn.unlistenMsgAll('clientMsgs/Input');
this.server.broadcastMsg('serverMsgs/Leave', {
uid: uid
}, this.conns);
}
applyInput(input: RoomInput) {
let playerIndex = this.state.players.findIndex(v => v.uid === input.uid);
if (playerIndex > -1) {
this.state.players[playerIndex] = applyPlayerInput(this.state.players[playerIndex], input.input);
}
}
}
export interface RoomInput {
uid: number,
input: PlayerInput
}
declare module 'tsrpc' {
export interface WsConnection {
uid?: number;
}
}

View File

@ -1,7 +0,0 @@
// This is a demo code file
// Feel free to delete it
export interface MsgChat {
content: string,
time: Date
}

View File

@ -0,0 +1,14 @@
import { PlayerState } from "../states/Player";
import { RoomState } from "../states/RoomState";
export interface ReqJoinRoom {
nickname: string;
skinId: number;
}
export interface ResJoinRoom {
uid: number,
roomState: RoomState
}
// export const conf = {}

View File

@ -1,10 +0,0 @@
// This is a demo code file
// Feel free to delete it
export interface ReqSend {
content: string
}
export interface ResSend {
time: Date
}

View File

@ -1,15 +0,0 @@
export interface BaseRequest {
}
export interface BaseResponse {
}
export interface BaseConf {
}
export interface BaseMessage {
}

View File

@ -0,0 +1,3 @@
import { PlayerInput } from "../../states/Player";
export type MsgInput = { sn: number } & PlayerInput;

View File

@ -0,0 +1,8 @@
import { MsgInput } from "../clientMsgs/MsgInput";
export interface MsgFrame {
inputs: {
uid: number,
msgInput: MsgInput
}[]
}

View File

@ -0,0 +1,5 @@
import { PlayerState } from "../../states/Player";
export interface MsgJoin {
player: PlayerState;
}

View File

@ -0,0 +1,3 @@
export interface MsgLeave {
uid: number;
}

View File

@ -1,75 +1,279 @@
import { ServiceProto } from 'tsrpc-proto'; import { ServiceProto } from 'tsrpc-proto';
import { MsgChat } from './MsgChat'; import { MsgInput } from './clientMsgs/MsgInput';
import { ReqSend, ResSend } from './PtlSend'; import { ReqJoinRoom, ResJoinRoom } from './PtlJoinRoom';
import { MsgFrame } from './serverMsgs/MsgFrame';
// This is a demo service proto file (auto generated) import { MsgJoin } from './serverMsgs/MsgJoin';
// Feel free to delete it import { MsgLeave } from './serverMsgs/MsgLeave';
export interface ServiceType { export interface ServiceType {
api: { api: {
"Send": { "JoinRoom": {
req: ReqSend, req: ReqJoinRoom,
res: ResSend res: ResJoinRoom
} }
}, },
msg: { msg: {
"Chat": MsgChat "clientMsgs/Input": MsgInput,
"serverMsgs/Frame": MsgFrame,
"serverMsgs/Join": MsgJoin,
"serverMsgs/Leave": MsgLeave
} }
} }
export const serviceProto: ServiceProto<ServiceType> = { export const serviceProto: ServiceProto<ServiceType> = {
"version": 3,
"services": [ "services": [
{ {
"id": 0, "id": 2,
"name": "Chat", "name": "clientMsgs/Input",
"type": "msg" "type": "msg"
}, },
{ {
"id": 1, "id": 3,
"name": "Send", "name": "JoinRoom",
"type": "api" "type": "api"
},
{
"id": 7,
"name": "serverMsgs/Frame",
"type": "msg"
},
{
"id": 4,
"name": "serverMsgs/Join",
"type": "msg"
},
{
"id": 5,
"name": "serverMsgs/Leave",
"type": "msg"
} }
], ],
"types": { "types": {
"MsgChat/MsgChat": { "clientMsgs/MsgInput/MsgInput": {
"type": "Intersection",
"members": [
{
"id": 0,
"type": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "sn",
"type": {
"type": "Number"
}
}
]
}
},
{
"id": 1,
"type": {
"type": "Reference",
"target": "../states/Player/PlayerInput"
}
}
]
},
"../states/Player/PlayerInput": {
"type": "Reference",
"target": "../states/Player/PlayerMove"
},
"../states/Player/PlayerMove": {
"type": "Interface", "type": "Interface",
"properties": [ "properties": [
{ {
"id": 0, "id": 0,
"name": "content", "name": "type",
"type": {
"type": "Literal",
"literal": "move"
}
},
{
"id": 1,
"name": "offset",
"type": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "x",
"type": {
"type": "Number"
}
},
{
"id": 1,
"name": "y",
"type": {
"type": "Number"
}
}
]
}
}
]
},
"PtlJoinRoom/ReqJoinRoom": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "nickname",
"type": { "type": {
"type": "String" "type": "String"
} }
}, },
{ {
"id": 1, "id": 1,
"name": "time", "name": "skinId",
"type": { "type": {
"type": "Date" "type": "Number"
} }
} }
] ]
}, },
"PtlSend/ReqSend": { "PtlJoinRoom/ResJoinRoom": {
"type": "Interface", "type": "Interface",
"properties": [ "properties": [
{ {
"id": 0, "id": 0,
"name": "content", "name": "uid",
"type": {
"type": "Number"
}
},
{
"id": 3,
"name": "roomState",
"type": {
"type": "Reference",
"target": "../states/RoomState/RoomState"
}
}
]
},
"../states/RoomState/RoomState": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "players",
"type": {
"type": "Array",
"elementType": {
"type": "Reference",
"target": "../states/Player/PlayerState"
}
}
}
]
},
"../states/Player/PlayerState": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "uid",
"type": {
"type": "Number"
}
},
{
"id": 1,
"name": "nickname",
"type": { "type": {
"type": "String" "type": "String"
} }
},
{
"id": 2,
"name": "skinId",
"type": {
"type": "Number"
}
},
{
"id": 3,
"name": "pos",
"type": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "x",
"type": {
"type": "Number"
}
},
{
"id": 1,
"name": "y",
"type": {
"type": "Number"
}
}
]
}
} }
] ]
}, },
"PtlSend/ResSend": { "serverMsgs/MsgFrame/MsgFrame": {
"type": "Interface", "type": "Interface",
"properties": [ "properties": [
{ {
"id": 0, "id": 0,
"name": "time", "name": "inputs",
"type": { "type": {
"type": "Date" "type": "Array",
"elementType": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "uid",
"type": {
"type": "Number"
}
},
{
"id": 1,
"name": "msgInput",
"type": {
"type": "Reference",
"target": "clientMsgs/MsgInput/MsgInput"
}
}
]
}
}
}
]
},
"serverMsgs/MsgJoin/MsgJoin": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "player",
"type": {
"type": "Reference",
"target": "../states/Player/PlayerState"
}
}
]
},
"serverMsgs/MsgLeave/MsgLeave": {
"type": "Interface",
"properties": [
{
"id": 0,
"name": "uid",
"type": {
"type": "Number"
} }
} }
] ]

View File

@ -0,0 +1,30 @@
export interface PlayerState {
uid: number,
nickname: string,
skinId: number,
// 可变状态
pos: {
x: number,
y: number
}
}
export interface PlayerMove {
type: 'move',
// 位移距离
offset: {
x: number,
y: number
}
}
export type PlayerInput = PlayerMove;
export function applyPlayerInput(state: PlayerState, input: PlayerInput): PlayerState {
if (input.type === 'move') {
state.pos.x += input.offset.x;
state.pos.y += input.offset.y;
}
return state;
}

View File

@ -0,0 +1,5 @@
import { PlayerState } from "./Player";
export interface RoomState {
players: PlayerState[]
}

View File

@ -1,4 +1,4 @@
import { CodeTemplate, TsrpcConfig } from 'tsrpc-cli'; import { TsrpcConfig } from 'tsrpc-cli';
const tsrpcConf: TsrpcConfig = { const tsrpcConf: TsrpcConfig = {
// Generate ServiceProto // Generate ServiceProto
@ -8,17 +8,17 @@ const tsrpcConf: TsrpcConfig = {
output: 'src/shared/protocols/serviceProto.ts', // Path for generated ServiceProto output: 'src/shared/protocols/serviceProto.ts', // Path for generated ServiceProto
apiDir: 'src/api', // API dir apiDir: 'src/api', // API dir
docDir: 'docs', // API documents dir docDir: 'docs', // API documents dir
ptlTemplate: CodeTemplate.getExtendedPtl(), // ptlTemplate: CodeTemplate.getExtendedPtl(),
// msgTemplate: CodeTemplate.getExtendedMsg(), // msgTemplate: CodeTemplate.getExtendedMsg(),
} }
], ],
// Sync shared code // Sync shared code
sync: [ sync: [
// { {
// from: 'src/shared', from: 'src/shared',
// to: '../frontend/src/shared', to: '../frontend/assets/scripts/shared',
// type: 'symlink' // Change this to 'copy' if your environment not support symlink type: 'symlink' // Change this to 'copy' if your environment not support symlink
// } }
], ],
// Dev server // Dev server
dev: { dev: {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "5ee75bff-6095-4266-a58b-9e215bf1e0a9",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "b0e73381-3db4-43a8-8dc3-7b6ba4dfcaf5",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,124 @@
{
"ver": "2.1.4",
"importer": "fbx",
"imported": true,
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f",
"files": [],
"subMetas": {
"f6845": {
"ver": "1.1.0",
"importer": "gltf-mesh",
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f@f6845",
"imported": true,
"files": [
".bin",
".json"
],
"subMetas": {},
"userData": {
"gltfIndex": 0
},
"displayName": "",
"id": "f6845",
"name": "newMap01.mesh"
},
"3500c": {
"ver": "1.0.3",
"importer": "gltf-embeded-image",
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f@3500c",
"imported": true,
"files": [
".jpg",
".json"
],
"subMetas": {},
"userData": {
"gltfIndex": 0
},
"displayName": "",
"id": "3500c",
"name": "newMap01.jpg.image"
},
"bfae8": {
"ver": "1.0.21",
"importer": "texture",
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f@bfae8",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "c26a63b9-5039-47ae-b027-ef2bce881f6f@3500c"
},
"displayName": "",
"id": "bfae8",
"name": "贴图 #3.texture"
},
"7ff75": {
"ver": "1.0.14",
"importer": "gltf-material",
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f@7ff75",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"gltfIndex": 0
},
"displayName": "",
"id": "7ff75",
"name": "Material #27.material"
},
"fc11b": {
"importer": "gltf-scene",
"uuid": "c26a63b9-5039-47ae-b027-ef2bce881f6f@fc11b",
"displayName": "",
"id": "fc11b",
"name": "newMap01.prefab",
"userData": {
"gltfIndex": 0
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"imageMetas": [
{
"name": "newMap01.jpg",
"uri": "c26a63b9-5039-47ae-b027-ef2bce881f6f@3500c"
}
],
"redirect": "c26a63b9-5039-47ae-b027-ef2bce881f6f@fc11b",
"assetFinder": {
"meshes": [
"c26a63b9-5039-47ae-b027-ef2bce881f6f@f6845"
],
"skeletons": [],
"textures": [
"c26a63b9-5039-47ae-b027-ef2bce881f6f@bfae8"
],
"materials": [
"c26a63b9-5039-47ae-b027-ef2bce881f6f@7ff75"
],
"scenes": [
"c26a63b9-5039-47ae-b027-ef2bce881f6f@fc11b"
]
},
"legacyFbxImporter": true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,41 @@
{
"ver": "1.0.21",
"importer": "image",
"imported": true,
"uuid": "27e1fcb7-5016-4252-8c43-ac9c1c97308e",
"files": [
".jpg",
".json"
],
"subMetas": {
"6c48a": {
"ver": "1.0.21",
"importer": "texture",
"uuid": "27e1fcb7-5016-4252-8c43-ac9c1c97308e@6c48a",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "27e1fcb7-5016-4252-8c43-ac9c1c97308e"
},
"displayName": "newMap01",
"id": "6c48a",
"name": "texture"
}
},
"userData": {
"type": "texture",
"redirect": "27e1fcb7-5016-4252-8c43-ac9c1c97308e@6c48a",
"hasAlpha": false
}
}

View File

@ -0,0 +1,55 @@
{
"__type__": "cc.Material",
"_name": "",
"_objFlags": 0,
"_native": "",
"_effectAsset": {
"__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319"
},
"_techIdx": 0,
"_defines": [
{
"USE_ALBEDO_MAP": true,
"ROUGHNESS_CHANNEL": "g",
"METALLIC_CHANNEL": "b",
"OCCLUSION_CHANNEL": "r"
}
],
"_states": [
{
"blendState": {
"targets": [
{}
]
},
"depthStencilState": {},
"rasterizerState": {}
}
],
"_props": [
{
"albedoScale": {
"__type__": "cc.Vec4",
"x": 1,
"y": 1,
"z": 1,
"w": 1
},
"pbrScale": {
"__type__": "cc.Vec4",
"x": 1,
"y": 0.242535620927811,
"z": 0.400000005960464,
"w": 1
},
"alphaThreshold": 1,
"normalStrenth": 1,
"mainTexture": {
"__uuid__": "27e1fcb7-5016-4252-8c43-ac9c1c97308e@6c48a"
},
"occlusion": 1,
"roughness": 0.242535620927811,
"metallic": 0.400000005960464
}
]
}

View File

@ -0,0 +1,11 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "b7d6d595-6cd1-43c6-8bc7-3a92644870b9",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "b62bf789-093f-4583-8530-86e00cd8a2a6",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,48 @@
{
"__type__": "cc.Material",
"_name": "",
"_objFlags": 0,
"_native": "",
"_effectAsset": {
"__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319",
"__expectedType__": "cc.EffectAsset"
},
"_techIdx": 0,
"_defines": [
{
"USE_INSTANCING": true,
"USE_ALBEDO_MAP": true
},
{},
{},
{}
],
"_states": [
{
"rasterizerState": {},
"depthStencilState": {},
"blendState": {
"targets": [
{}
]
}
},
{},
{},
{}
],
"_props": [
{
"alphaThreshold": 0,
"roughness": 0.70710676908493,
"metallic": 0.400000005960464,
"mainTexture": {
"__uuid__": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa@6c48a",
"__expectedType__": "cc.Texture2D"
}
},
{},
{},
{}
]
}

View File

@ -0,0 +1,11 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "225b969b-eb6a-4489-a5a5-08efbcf38b0e",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,48 @@
{
"__type__": "cc.Material",
"_name": "",
"_objFlags": 0,
"_native": "",
"_effectAsset": {
"__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319",
"__expectedType__": "cc.EffectAsset"
},
"_techIdx": 0,
"_defines": [
{
"USE_INSTANCING": true,
"USE_ALBEDO_MAP": true
},
{},
{},
{}
],
"_states": [
{
"rasterizerState": {},
"depthStencilState": {},
"blendState": {
"targets": [
{}
]
}
},
{},
{},
{}
],
"_props": [
{
"alphaThreshold": 0,
"roughness": 0.70710676908493,
"metallic": 0.400000005960464,
"mainTexture": {
"__uuid__": "f35bcb71-cd72-443c-94a9-5482c2d63d66@6c48a",
"__expectedType__": "cc.Texture2D"
}
},
{},
{},
{}
]
}

View File

@ -0,0 +1,11 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "411ae6be-9581-422b-9425-36c8e71651cb",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,312 @@
{
"ver": "2.1.4",
"importer": "fbx",
"imported": true,
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c",
"files": [
"__original-animation-0.cconb"
],
"subMetas": {
"95953": {
"importer": "gltf-material",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@95953",
"displayName": "",
"id": "95953",
"name": "Material #7.material",
"userData": {
"gltfIndex": 0
},
"ver": "1.0.14",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"6868c": {
"importer": "gltf-mesh",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@6868c",
"displayName": "",
"id": "6868c",
"name": "soldier01.mesh",
"userData": {
"gltfIndex": 0
},
"ver": "1.1.0",
"imported": true,
"files": [
".bin",
".json"
],
"subMetas": {}
},
"1f586": {
"importer": "gltf-animation",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@1f586",
"displayName": "",
"id": "1f586",
"name": "idle.animation",
"userData": {
"gltfIndex": 0,
"wrapMode": 2,
"sample": 30,
"span": {
"from": 0,
"to": 0.8
},
"events": []
},
"ver": "1.0.16",
"imported": true,
"files": [
".cconb"
],
"subMetas": {}
},
"cf5ee": {
"importer": "gltf-animation",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@cf5ee",
"displayName": "",
"id": "cf5ee",
"name": "run.animation",
"userData": {
"gltfIndex": 0,
"wrapMode": 2,
"sample": 60,
"span": {
"from": 0.8,
"to": 1.5
},
"events": []
},
"ver": "1.0.16",
"imported": true,
"files": [
".cconb"
],
"subMetas": {}
},
"989ed": {
"importer": "gltf-animation",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@989ed",
"displayName": "",
"id": "989ed",
"name": "attack.animation",
"userData": {
"gltfIndex": 0,
"wrapMode": 2,
"sample": 60,
"span": {
"from": 1.5333333333333334,
"to": 2.2
},
"events": [
{
"frame": 0.23333333333333334,
"func": "",
"params": []
},
{
"frame": 0.23333333333333334,
"func": "triggerEffect",
"params": []
}
],
"speed": 1
},
"ver": "1.0.16",
"imported": true,
"files": [
".cconb"
],
"subMetas": {}
},
"ee525": {
"importer": "gltf-animation",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@ee525",
"displayName": "",
"id": "ee525",
"name": "win.animation",
"userData": {
"gltfIndex": 0,
"wrapMode": 2,
"sample": 30,
"span": {
"from": 2.2,
"to": 2.8
},
"events": []
},
"ver": "1.0.16",
"imported": true,
"files": [
".cconb"
],
"subMetas": {}
},
"5b2e9": {
"importer": "gltf-animation",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@5b2e9",
"displayName": "",
"id": "5b2e9",
"name": "died.animation",
"userData": {
"gltfIndex": 0,
"wrapMode": 2,
"sample": 30,
"span": {
"from": 2.8,
"to": 4.333333333333333
},
"events": []
},
"ver": "1.0.16",
"imported": true,
"files": [
".cconb"
],
"subMetas": {}
},
"438fe": {
"importer": "gltf-skeleton",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@438fe",
"displayName": "",
"id": "438fe",
"name": "UnnamedSkeleton.skeleton",
"userData": {
"gltfIndex": 0,
"jointsLength": 6
},
"ver": "1.0.1",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"fa1d4": {
"importer": "gltf-embeded-image",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@fa1d4",
"displayName": "",
"id": "fa1d4",
"name": "soldier01.jpg.image",
"userData": {
"gltfIndex": 0
},
"ver": "1.0.3",
"imported": true,
"files": [
".jpg",
".json"
],
"subMetas": {}
},
"291d4": {
"importer": "texture",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@291d4",
"displayName": "",
"id": "291d4",
"name": "贴图 #6.texture",
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "57d4c4ef-3199-4596-bf79-7c065964ca9c@fa1d4"
},
"ver": "1.0.21",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"33dba": {
"importer": "gltf-scene",
"uuid": "57d4c4ef-3199-4596-bf79-7c065964ca9c@33dba",
"displayName": "",
"id": "33dba",
"name": "soldier01.prefab",
"userData": {
"gltfIndex": 0
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"imageMetas": [
{
"name": "soldier01.jpg",
"uri": "57d4c4ef-3199-4596-bf79-7c065964ca9c@fa1d4"
}
],
"animationImportSettings": [
{
"name": "Take 001",
"duration": 4.666666507720947,
"fps": 30,
"splits": [
{
"name": "idle",
"from": 0,
"to": 0.8,
"wrapMode": 2
},
{
"name": "run",
"from": 0.8,
"to": 1.5,
"fps": 60,
"wrapMode": 2
},
{
"name": "attack",
"from": 1.5333333333333334,
"to": 2.2,
"fps": 60,
"wrapMode": 2
},
{
"name": "win",
"from": 2.2,
"to": 2.8,
"wrapMode": 2
},
{
"name": "died",
"from": 2.8,
"to": 4.333333333333333,
"wrapMode": 2
}
]
}
],
"redirect": "57d4c4ef-3199-4596-bf79-7c065964ca9c@33dba",
"assetFinder": {
"meshes": [
"57d4c4ef-3199-4596-bf79-7c065964ca9c@6868c"
],
"skeletons": [
"57d4c4ef-3199-4596-bf79-7c065964ca9c@438fe"
],
"textures": [
"57d4c4ef-3199-4596-bf79-7c065964ca9c@291d4"
],
"materials": [
"57d4c4ef-3199-4596-bf79-7c065964ca9c@95953"
],
"scenes": [
"57d4c4ef-3199-4596-bf79-7c065964ca9c@33dba"
]
},
"legacyFbxImporter": true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,41 @@
{
"ver": "1.0.21",
"importer": "image",
"imported": true,
"uuid": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa",
"files": [
".jpg",
".json"
],
"subMetas": {
"6c48a": {
"ver": "1.0.21",
"importer": "texture",
"uuid": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa@6c48a",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa"
},
"displayName": "soldier01",
"id": "6c48a",
"name": "texture"
}
},
"userData": {
"type": "texture",
"redirect": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa@6c48a",
"hasAlpha": false
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,41 @@
{
"ver": "1.0.21",
"importer": "image",
"imported": true,
"uuid": "f35bcb71-cd72-443c-94a9-5482c2d63d66",
"files": [
".jpg",
".json"
],
"subMetas": {
"6c48a": {
"ver": "1.0.21",
"importer": "texture",
"uuid": "f35bcb71-cd72-443c-94a9-5482c2d63d66@6c48a",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "f35bcb71-cd72-443c-94a9-5482c2d63d66"
},
"displayName": "soldier02",
"id": "6c48a",
"name": "texture"
}
},
"userData": {
"type": "texture",
"redirect": "f35bcb71-cd72-443c-94a9-5482c2d63d66@6c48a",
"hasAlpha": false
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,41 @@
{
"ver": "1.0.21",
"importer": "image",
"imported": true,
"uuid": "8b8d74a3-bdf7-4c4d-8334-2cb7e0f7bf88",
"files": [
".jpg",
".json"
],
"subMetas": {
"6c48a": {
"ver": "1.0.21",
"importer": "texture",
"uuid": "8b8d74a3-bdf7-4c4d-8334-2cb7e0f7bf88@6c48a",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "repeat",
"wrapModeT": "repeat",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "8b8d74a3-bdf7-4c4d-8334-2cb7e0f7bf88"
},
"displayName": "soldier03",
"id": "6c48a",
"name": "texture"
}
},
"userData": {
"type": "texture",
"redirect": "8b8d74a3-bdf7-4c4d-8334-2cb7e0f7bf88@6c48a",
"hasAlpha": false
}
}

View File

@ -0,0 +1,37 @@
{
"__type__": "cc.Material",
"_name": "",
"_objFlags": 0,
"_native": "",
"_effectAsset": {
"__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319"
},
"_techIdx": 0,
"_defines": [
{
"USE_SKINNING": 2,
"USE_ALBEDO_MAP": true
}
],
"_states": [
{
"blendState": {
"targets": [
{}
]
},
"depthStencilState": {},
"rasterizerState": {}
}
],
"_props": [
{
"alphaThreshold": 0,
"roughness": 0.70710676908493,
"metallic": 0.400000005960464,
"mainTexture": {
"__uuid__": "8b8d74a3-bdf7-4c4d-8334-2cb7e0f7bf88@6c48a"
}
}
]
}

View File

@ -0,0 +1,11 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "5620e8a2-444d-4e80-9eb8-f6f54bd4d3cf",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1 @@
import 'k8w-extend-native';

View File

@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "0688c02f-a4df-4cc9-8622-d4fce5561390",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,88 @@
/**
* UI v3.0 使 node.layer
* Creator 使 layer UI UI
* UI layer .
*
* UI rendering has changed in v3.0 to use node.layer to determine visibility.
* To ensure consistent performance after upgrading old projects.
* Creator will dynamically assign an unused layer to the UI node in the persist node at
* runtime to avoid conflicts between the layer of UI in the persist node and the
* layer of other UI in the scene. You can remove this script when you
* are sure there is no conflict
*/
import { _decorator, Node, director, Director, game, BaseNode, Canvas, Camera } from 'cc';
import { EDITOR } from 'cc/env';
const customLayerMask = 0x000fffff;
const builtinLayerMask = 0xfff00000;
director.on(Director.EVENT_AFTER_SCENE_LAUNCH, () => {
const roots = director.getScene()?.children as BaseNode[];
let allCanvases = director.getScene()?.getComponentsInChildren(Canvas) as Canvas[];
if (allCanvases.length <= 1) return;
allCanvases = allCanvases.filter(x => !!x.cameraComponent);
let allCameras = director.getScene()?.getComponentsInChildren(Camera) as Camera[];
let usedLayer = 0;
allCameras.forEach(x => usedLayer |= (x.visibility & customLayerMask));
const persistCanvas: Canvas[] = [];
for (let i = 0, l = roots.length; i < l; i++) {
const root = roots[i];
if (!game.isPersistRootNode(root)) continue;
const canvases = root.getComponentsInChildren(Canvas);
if (canvases.length === 0) continue;
persistCanvas.push(...canvases.filter(x => !!x.cameraComponent));
}
persistCanvas.forEach((val) => {
const isLayerCollided = allCanvases.find(x => x !== val && (x.cameraComponent!.visibility & val.cameraComponent!.visibility & customLayerMask));
if (isLayerCollided) {
const availableLayers = ~usedLayer;
const lastAvailableLayer = availableLayers & ~(availableLayers - 1);
val.cameraComponent!.visibility = lastAvailableLayer | (val.cameraComponent!.visibility & builtinLayerMask);
setChildrenLayer(val.node, lastAvailableLayer);
usedLayer |= availableLayers;
}
});
});
function setChildrenLayer (node: Node, layer: number) {
for (let i = 0, l = node.children.length; i < l; i++) {
node.children[i].layer = layer;
setChildrenLayer(node.children[i], layer);
}
}
let setParentEngine = Node.prototype.setParent;
if(!EDITOR) {
Node.prototype.setParent = function(value, keepWorldTransform) {
setParentEngine.call(this, value, keepWorldTransform);
if (!value) return;
// find canvas
let layer = getCanvasCameraLayer(this);
if (layer) {
this.layer = layer;
setChildrenLayer(this, layer);
}
}
}
function getCanvasCameraLayer (node: Node) {
let layer = 0;
let canvas = node.getComponent(Canvas);
if (canvas && canvas.cameraComponent) {
if (canvas.cameraComponent.visibility & canvas.node.layer) {
layer = canvas.node.layer;
} else {
layer = canvas.cameraComponent.visibility & ~(canvas.cameraComponent.visibility - 1);
}
return layer;
}
if (node.parent) {
layer = getCanvasCameraLayer(node.parent);
}
return layer;
}

View File

@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "3f14173e-54a7-423d-9c22-0efe5a1c634f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "0b89f62b-f696-4a77-809d-aa85c3178bf2",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "dfd416a2-ba3c-49d2-8200-2ed5fcd41292",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,511 @@
[
{
"__type__": "cc.Prefab",
"_name": "",
"_objFlags": 0,
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"asyncLoadAssets": false
},
{
"__type__": "cc.Node",
"_name": "Joystick",
"_objFlags": 0,
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 18
},
{
"__id__": 20
},
{
"__id__": 22
}
],
"_prefab": {
"__id__": 24
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -480,
"y": -320,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_layer": 524288,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "disk",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_children": [
{
"__id__": 3
},
{
"__id__": 9
}
],
"_active": true,
"_components": [
{
"__id__": 15
}
],
"_prefab": {
"__id__": 17
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 299.003,
"y": 440.043,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_layer": 524288,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "base",
"_objFlags": 0,
"_parent": {
"__id__": 2
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 4
},
{
"__id__": 6
}
],
"_prefab": {
"__id__": 8
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_layer": 524288,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 5
},
"_contentSize": {
"__type__": "cc.Size",
"width": 256,
"height": 256
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "daHOQp4LdBFoenok+XY1Zo"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 7
},
"_visFlags": 0,
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 40
},
"_spriteFrame": {
"__uuid__": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@f9941",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "b4yQwCjWpMRZUnITwiaJW8"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "99DpUW4QRIJ4zS/1nooi0x"
},
{
"__type__": "cc.Node",
"_name": "stick",
"_objFlags": 0,
"_parent": {
"__id__": 2
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 10
},
{
"__id__": 12
}
],
"_prefab": {
"__id__": 14
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 0.238,
"y": 0.238,
"z": 0.238
},
"_layer": 524288,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 9
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"_contentSize": {
"__type__": "cc.Size",
"width": 256,
"height": 256
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "7fgMfPZ3pIZ72y8mESbyAp"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 9
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"_visFlags": 0,
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 200
},
"_spriteFrame": {
"__uuid__": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@f9941",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "d8pnWua/5KaYBq0ggPBL1Y"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "ceQ/tc9/lBPIsW1Hwyb91a"
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 16
},
"_contentSize": {
"__type__": "cc.Size",
"width": 256,
"height": 256
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f8l1UBgYFPm7f/KMYDjdJo"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "d0wZ2A2kRHRL52JXTqW2TD"
},
{
"__type__": "cc.UITransform",
"_name": "Joystick<UITransform>",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 19
},
"_contentSize": {
"__type__": "cc.Size",
"width": 960,
"height": 640
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "03ZUWxyGxEKrRNfAAZAjfl"
},
{
"__type__": "cc.Widget",
"_name": "Joystick<Widget>",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 21
},
"_alignFlags": 45,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 40,
"_originalHeight": 36,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "dfw8DiwVhOMqNxW3/MvA/u"
},
{
"__type__": "ccc7fTjElVEQqB07I1JvC+N",
"_name": "Joystick<Joystick>",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 23
},
"radius": 128,
"disk": {
"__id__": 2
},
"stick": {
"__id__": 9
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "b6/h8KXgpAI6dQQRwusaRr"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "25fHuwytBN2qk6dH5oVJvO"
}
]

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.32",
"importer": "prefab",
"imported": true,
"uuid": "0363f7f8-204e-410e-ade0-03adca6ea835",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "Joystick"
}
}

View File

@ -0,0 +1,90 @@
import { _decorator, Component, Node, EventTouch, tween, Vec2, Vec3 } from 'cc';
import { MathUtil } from '../../../scripts/models/MathUtil';
const { ccclass, property } = _decorator;
const v3_1 = new Vec3;
const v3_2 = new Vec3;
export interface JoystickOptions {
onOperate: (output: JoystickOutput) => void,
onOperateEnd: () => void,
}
export interface JoystickOutput {
// 0 ~ 1
x: number,
// 0 ~ 1
y: number
}
@ccclass('Joystick')
export class Joystick extends Component {
@property
radius: number = 128;
@property(Node)
disk: Node = null as any;
@property(Node)
stick: Node = null as any;
private _options!: JoystickOptions;
public get options(): JoystickOptions {
return this._options;
}
public set options(v: JoystickOptions) {
this._options = v;
}
onLoad() {
this.node.on(Node.EventType.TOUCH_START, this.onTouch, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.onTouch, this);
this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.disk.active = false;
}
private _touchStartPos?: Vec2;
onTouch(e: EventTouch) {
this.disk.active = true;
let loc = e.touch.getUILocation();
if (!this._touchStartPos) {
this._touchStartPos = loc.clone();
this.disk.setPosition(loc.x, loc.y, 0);
}
let diskPos = this.disk.position;
let stickPos: Vec3 = v3_1.set(loc.x - diskPos.x, loc.y - diskPos.y, 0);
let length = stickPos.length();
if (length === 0) {
this.stick.setPosition(0, 0, 0);
return;
}
let newLength = MathUtil.limit(length, 0, this.radius);
stickPos.multiplyScalar(newLength / length);
// if (length > newLength) {
// let newDiskPos = this.disk.position.clone().add(stickPos);
// this.disk.setPosition(newDiskPos);
// }
this.stick.setPosition(stickPos);
stickPos.normalize();
this.options?.onOperate({
x: stickPos.x || 0,
y: stickPos.y || 0
})
}
onTouchEnd(e: EventTouch) {
this.disk.active = false;
this._touchStartPos = undefined;
this.options?.onOperateEnd();
}
}

View File

@ -0,0 +1,13 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "ccc7f4e3-1255-4442-a074-ec8d49bc2f8d",
"files": [],
"subMetas": {},
"userData": {
"importAsPlugin": false,
"moduleId": "project:///assets/scenes/GameScene/scripts/Joystick.js",
"simulateGlobals": []
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "ceb8a41c-a457-4ec0-b9a3-f02af963096e",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "b27b7c33-0d34-4728-84cb-cd4dae339cbd",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "c9e83364-8449-4378-8949-7580d6c2e980",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,11 @@
{
"ver": "1.1.32",
"importer": "scene",
"imported": true,
"uuid": "0d3889f6-dc9c-424e-b8cd-6fa78d63af15",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "f40f793d-269b-44f4-b754-684d7d93892c",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,101 @@
import { WsClient } from 'tsrpc-browser';
import { MsgInput } from './shared/protocols/clientMsgs/MsgInput';
import { MsgFrame } from './shared/protocols/serverMsgs/MsgFrame';
import { ServiceType } from "./shared/protocols/serviceProto";
import { applyPlayerInput, PlayerInput, PlayerState } from "./shared/states/Player";
import { RoomState } from './shared/states/RoomState';
/**
* - -
*/
export class Room {
state: RoomState = {
players: []
}
client: WsClient<ServiceType>;
self?: {
uid: number,
// 最后一次权威状态
lastServerState: PlayerState
}
private _lastSendInputSN = 0;
private _sendingMsgs: MsgInput[] = [];
constructor(client: WsClient<ServiceType>) {
this.client = client;
this.client.listenMsg('serverMsgs/Frame', msg => {
this.applyServerFrame(msg);
});
this.client.listenMsg('serverMsgs/Join', msg => {
this.state.players.push(msg.player);
});
this.client.listenMsg('serverMsgs/Leave', msg => {
this.state.players.removeOne(v => v.uid === msg.uid);
});
}
init(roomState: RoomState) {
}
/** 同步服务端的权威消息 */
applyServerFrame(msg: MsgFrame) {
msg.inputs.forEach(({ uid, msgInput }) => {
let playerIndex = this.state.players.findIndex(v => v.uid === uid);
if (playerIndex === -1) {
return;
}
let newPlayer: PlayerState = this.state.players[playerIndex];
// 自己:和解
if (uid === this.self?.uid) {
this._sendingMsgs.remove(v => v.sn <= msgInput.sn);
this.state.players[playerIndex] = this.self.lastServerState;
// 预测
this._sendingMsgs.forEach(v => {
this.state.players[playerIndex] = applyPlayerInput(this.state.players[playerIndex], v);
})
}
// 其它人:直接同步
else {
this.state.players[playerIndex] = applyPlayerInput(this.state.players[playerIndex], msgInput);
}
});
}
/** 发送客户端输入,并执行本地预测 */
sendInput(input: PlayerInput) {
if (!this.self) {
return;
}
let msg: MsgInput = {
...input,
sn: ++this._lastSendInputSN
}
this._sendingMsgs.push(msg);
this.client.sendMsg('clientMsgs/Input', msg);
}
async joinRoom() {
let ret = await this.client.callApi('JoinRoom', {
nickname: 'xxx',
skinId: 1
});
if (!ret.isSucc) {
alert(ret.err.message);
return;
}
this.self = {
uid: ret.res.uid,
lastServerState: ret.res.roomState.players.find(v => v.uid === ret.res!.uid)!
}
this.state = ret.res.roomState;
}
}

View File

@ -0,0 +1,9 @@
{
"ver": "4.0.22",
"importer": "typescript",
"imported": true,
"uuid": "45fa3b70-b2ce-40a3-882a-79fbe5e7ae62",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@ -0,0 +1 @@
../../../backend/src/shared

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "78099e0e-0105-4286-9a60-40235d454bce",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -0,0 +1,12 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "60d130b7-07a2-41e9-985d-b5511bec8941",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,75 @@
{
"ver": "1.0.21",
"importer": "image",
"imported": true,
"uuid": "521ad970-c5d9-4d2d-af6f-cc08f94230f8",
"files": [
".png",
".json"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@6c48a",
"displayName": "circle256",
"id": "6c48a",
"name": "texture",
"ver": "1.0.21",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"premultiplyAlpha": false,
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "521ad970-c5d9-4d2d-af6f-cc08f94230f8"
}
},
"f9941": {
"importer": "sprite-frame",
"uuid": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@f9941",
"displayName": "circle256",
"id": "f9941",
"name": "spriteFrame",
"ver": "1.0.9",
"imported": true,
"files": [
".json"
],
"subMetas": {},
"userData": {
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 256,
"height": 256,
"rawWidth": 256,
"rawHeight": 256,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@6c48a",
"atlasUuid": "",
"packable": true
}
}
},
"userData": {
"type": "sprite-frame",
"redirect": "521ad970-c5d9-4d2d-af6f-cc08f94230f8@f9941",
"hasAlpha": true
}
}

View File

@ -5,5 +5,8 @@
"version": "3.3.2", "version": "3.3.2",
"creator": { "creator": {
"version": "3.3.2" "version": "3.3.2"
},
"dependencies": {
"tsrpc-browser": "^3.1.2"
} }
} }

View File

@ -1,3 +1,9 @@
{ {
"__version__": "1.0.1" "__version__": "1.0.1",
"layer": [
{
"name": "canvas_19",
"value": 524288
}
]
} }

View File

@ -1,9 +1,6 @@
{ {
/* Base configuration. Do not edit this field. */ /* Base configuration. Do not edit this field. */
"extends": "./temp/tsconfig.cocos.json", "extends": "./temp/tsconfig.cocos.json",
/* Add your custom configuration here. */ /* Add your custom configuration here. */
"compilerOptions": { "compilerOptions": {}
"strict": false
}
} }