├── .laya ├── compile.js ├── launch.json ├── publish.js ├── publish_oppogame.js ├── publish_xmgame.js └── settings.json ├── bin ├── DP1.png ├── fileconfig.json ├── game.js ├── game.json ├── index.html ├── index.js ├── joystickView.json ├── js │ └── bundle.js ├── libs │ ├── bytebuffer.js │ ├── domparserinone.js │ ├── laya.ani.js │ ├── laya.bdmini.js │ ├── laya.core.js │ ├── laya.d3.js │ ├── laya.d3Plugin.js │ ├── laya.debugtool.js │ ├── laya.device.js │ ├── laya.effect.js │ ├── laya.html.js │ ├── laya.particle.js │ ├── laya.pathfinding.js │ ├── laya.physics.js │ ├── laya.physics3D.js │ ├── laya.physics3D.runtime.js │ ├── laya.physics3D.wasm.js │ ├── laya.physics3D.wasm.wasm │ ├── laya.quickgamemini.js │ ├── laya.tiledmap.js │ ├── laya.ui.js │ ├── laya.wxmini.js │ ├── laya.xmmini.js │ └── worker.js ├── mainView.json ├── project.config.json ├── project.swan.json ├── res │ └── atlas │ │ └── .rec ├── swan-game-adapter.js ├── tankke.png ├── ui.json ├── unpack.json ├── version.json ├── weapp-adapter.js └── yangan.png ├── joystick.laya ├── laya ├── .laya ├── assets │ ├── DP1.png │ ├── tankke.png │ └── yangan.png ├── ignore.cfg └── pages │ ├── joystickView.scene │ └── mainView.scene ├── libs ├── LayaAir.d.ts ├── layaAir.minigame.d.ts ├── union.d.ts └── wx.d.ts ├── src ├── GameConfig.ts ├── JoyStick.ts ├── Main.ts └── ui │ └── layaMaxUI.ts └── tsconfig.json /.laya/compile.js: -------------------------------------------------------------------------------- 1 | // v1.0.0 2 | //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行) 3 | let useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false; 4 | //获取Node插件和工作路径 5 | let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : ""; 6 | let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\compile.js", "").replace("/.laya/compile.js", "") : "./../"; 7 | 8 | //引用插件模块 9 | let gulp = require(ideModuleDir + "gulp"); 10 | let browserify = require(ideModuleDir + "browserify"); 11 | let source = require(ideModuleDir + "vinyl-source-stream"); 12 | let tsify = require(ideModuleDir + "tsify"); 13 | 14 | // 如果是发布时调用编译功能,增加prevTasks 15 | let prevTasks = ""; 16 | if (global.publish) { 17 | prevTasks = ["loadConfig"]; 18 | } 19 | 20 | //使用browserify,转换ts到js,并输出到bin/js目录 21 | gulp.task("compile", prevTasks, function () { 22 | // 发布时调用编译功能,判断是否点击了编译选项 23 | if (global.publish && !global.config.compile) { 24 | return; 25 | } else if (global.publish && global.config.compile) { 26 | // 发布时调用编译,workSpaceDir使用publish.js里的变量 27 | workSpaceDir = global.workSpaceDir; 28 | } 29 | return browserify({ 30 | basedir: workSpaceDir, 31 | //是否开启调试,开启后会生成jsmap,方便调试ts源码,但会影响编译速度 32 | debug: true, 33 | entries: ['src/Main.ts'], 34 | cache: {}, 35 | packageCache: {} 36 | }) 37 | //使用tsify插件编译ts 38 | .plugin(tsify) 39 | .bundle() 40 | //使用source把输出文件命名为bundle.js 41 | .pipe(source('bundle.js')) 42 | //把bundle.js复制到bin/js目录 43 | .pipe(gulp.dest(workSpaceDir + "/bin/js")); 44 | }); -------------------------------------------------------------------------------- /.laya/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "layaAir", 6 | "type": "chrome", 7 | "request": "launch", 8 | "file": "${workspaceRoot}/bin/index.html", 9 | "runtimeExecutable": "${execPath}", 10 | "useBuildInServer": true, 11 | "sourceMaps": true, 12 | "webRoot": "${workspaceRoot}", 13 | "port": 9222, 14 | "fixedPort":false, 15 | "sourceMapPathOverrides": { 16 | "src/*": "${workspaceRoot}/src/*" 17 | } 18 | }, 19 | { 20 | "name": "chrome调试", 21 | "type": "chrome", 22 | "request": "launch", 23 | "file": "${workspaceRoot}/bin/index.html", 24 | // "换成自己的谷歌安装路径,": 比如 25 | //window 默认安装路径为: "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe" 26 | //mac 系统上的默认安装路径为 "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; 27 | // "runtimeExecutable": "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe", 28 | "runtimeArgs": [ 29 | "--allow-file-access-from-files", 30 | "--allow-file-access-frome-files", 31 | " --disable-web-security" 32 | ], 33 | "sourceMaps": true, 34 | "webRoot": "${workspaceRoot}", 35 | //假如谷歌调试报userDataDir不可用,请把谷歌安装路径取得管理员权限,或者更换${tmpdir}为其他可以读写的文件夹,也可以删除。 36 | "userDataDir": "${workspaceRoot}/.laya/chrome", 37 | "fixedPort":false, 38 | "sourceMapPathOverrides": { 39 | "src/*": "${workspaceRoot}/src/*" 40 | } 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /.laya/publish.js: -------------------------------------------------------------------------------- 1 | // v1.2.0 2 | //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行) 3 | const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false; 4 | //获取Node插件和工作路径 5 | let ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : ""; 6 | let workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish.js", "").replace("/.laya/publish.js", "") + "/" : "./../"; 7 | 8 | //引用插件模块 9 | const gulp = require(ideModuleDir + "gulp"); 10 | const fs = require("fs"); 11 | const path = require("path"); 12 | const uglify = require(ideModuleDir + "gulp-uglify"); 13 | const jsonminify = require(ideModuleDir + "gulp-jsonminify"); 14 | const image = require(ideModuleDir + "gulp-image"); 15 | const rev = require(ideModuleDir + "gulp-rev"); 16 | const revdel = require(ideModuleDir + "gulp-rev-delete-original"); 17 | const revCollector = require(ideModuleDir + 'gulp-rev-collector'); 18 | const del = require(ideModuleDir + "del"); 19 | const requireDir = require(ideModuleDir + 'require-dir'); 20 | 21 | global.ideModuleDir = ideModuleDir; 22 | global.workSpaceDir = workSpaceDir; 23 | 24 | // 结合compile.js使用 25 | global.publish = true; 26 | const fileList = ["compile.js", "publish_xmgame.js", "publish_oppogame.js"]; 27 | requireDir('./', { 28 | filter: function (fullPath) { 29 | // 只用到了compile.js和publish.js 30 | if (fileList.includes(path.basename(fullPath))) { 31 | return true; 32 | } else { 33 | return false; 34 | } 35 | } 36 | }); 37 | 38 | // 清理临时文件夹,加载配置 39 | let config, 40 | releaseDir, 41 | platform, 42 | isOpendataProj = false; 43 | gulp.task("loadConfig", function () { 44 | platform = "web" 45 | if (!useIDENode && process.argv.length > 5 && process.argv[4] == "--config") { 46 | platform = process.argv[5].replace(".json", ""); 47 | } 48 | if (useIDENode && process.argv.length >= 4 && process.argv[3].startsWith("--config") && process.argv[3].endsWith(".json")) { 49 | platform = process.argv[3].match(/(\w+).json/)[1]; 50 | } 51 | let _path; 52 | if (!useIDENode) { 53 | _path = platform + ".json"; 54 | releaseDir = "../release/" + platform; 55 | } 56 | if (useIDENode) { 57 | _path = path.join(workSpaceDir, ".laya", `${platform}.json`); 58 | releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/"); 59 | } 60 | global.platform = platform; 61 | let file = fs.readFileSync(_path, "utf-8"); 62 | if (file) { 63 | file = file.replace(/\$basePath/g, releaseDir); 64 | config = JSON.parse(file); 65 | global.config = config; 66 | } 67 | // 是否是开放域项目 68 | let projInfoPath = path.join(workSpaceDir, path.basename(workSpaceDir) + ".laya"); 69 | let isExist = fs.existsSync(projInfoPath); 70 | if (isExist) { 71 | try { 72 | let projInfo = fs.readFileSync(projInfoPath, "utf8"); 73 | projInfo = projInfo && JSON.parse(projInfo); 74 | isOpendataProj = projInfo.layaProType === 12; 75 | } catch (e) {} 76 | } 77 | }); 78 | 79 | // 重新编译项目 80 | // gulp.task("compile", ["loadConfig"], function () { 81 | // if (config.compile) { 82 | // console.log("compile"); 83 | // } 84 | // }); 85 | 86 | // 清理release文件夹 87 | gulp.task("clearReleaseDir", ["compile"], function (cb) { 88 | if (config.clearReleaseDir) { 89 | let delList = [releaseDir, releaseDir + "_pack", config.packfileTargetValue]; 90 | // 小米快游戏,使用即存的项目,删掉Laya工程文件,保留小米环境项目文件 91 | if (platform === "xmgame") { 92 | let xmProjSrc = path.join(releaseDir, config.xmInfo.projName); 93 | // 不要删掉manifest.json/main.js文件 94 | // 这里不是node-glob语法,详见: https://github.com/sindresorhus/del 95 | delList = [`${xmProjSrc}/**`, `!${xmProjSrc}`, `!${xmProjSrc}/node_modules/**`, `!${xmProjSrc}/sign/**`, `!${xmProjSrc}/{babel.config.js,main.js,manifest.json,package.json,package-lock.json}`]; 96 | } else if (platform === "oppogame") { 97 | let oppoProjSrc = path.join(releaseDir, config.oppoInfo.projName); 98 | delList = [`${oppoProjSrc}/**`, `!${oppoProjSrc}`, `!${oppoProjSrc}/dist/**`, `!${oppoProjSrc}/{manifest.json}`]; 99 | } 100 | del(delList, { force: true }).then(paths => { 101 | cb(); 102 | }); 103 | } else cb(); 104 | }); 105 | 106 | // copy bin文件到release文件夹 107 | gulp.task("copyFile", ["clearReleaseDir"], function () { 108 | let baseCopyFilter = [`${workSpaceDir}/bin/**/*.*`]; 109 | // 只拷贝index.js中引用的类库 110 | if (config.onlyIndexJS) { 111 | baseCopyFilter = baseCopyFilter.concat(`!${workSpaceDir}/bin/libs/*.*`); 112 | } 113 | if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,仅拷贝用到的文件 114 | config.copyFilesFilter = [`${workSpaceDir}/bin/js/bundle.js`, `${workSpaceDir}/bin/index.js`, `${workSpaceDir}/bin/game.js`]; 115 | if (config.projectType !== "as") { // 开放域精简类库 116 | config.copyFilesFilter.push(`${workSpaceDir}/bin/libs/laya.opendata.min.js`); 117 | } 118 | } else if (platform === "wxgame") { // 微信项目,不拷贝index.html,不拷贝百度bin目录中的文件 119 | config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.swan.json,swan-game-adapter.js}`]); 120 | } else if (platform === "bdgame") { // 百度项目,不拷贝index.html,不拷贝微信bin目录中的文件 121 | config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/index.html`, `!${workSpaceDir}/bin/{project.config.json,weapp-adapter.js}`]); 122 | } else { // web|QQ项目|小米快游戏,不拷贝微信、百度在bin目录中的文件 123 | config.copyFilesFilter = baseCopyFilter.concat([`!${workSpaceDir}/bin/{game.js,game.json,project.config.json,weapp-adapter.js,project.swan.json,swan-game-adapter.js}`]); 124 | } 125 | // 小米快游戏,需要新建一个快游戏项目,拷贝的只是项目的一部分,将文件先拷贝到文件夹的临时目录中去 126 | let QUICKGAMELIST = ["xmgame", "oppogame"]; 127 | if (QUICKGAMELIST.includes(platform)) { 128 | releaseDir = global.tempReleaseDir = path.join(releaseDir, "temprelease"); 129 | } 130 | global.releaseDir = releaseDir; 131 | var stream = gulp.src(config.copyFilesFilter, { base: `${workSpaceDir}/bin` }); 132 | return stream.pipe(gulp.dest(releaseDir)); 133 | }); 134 | 135 | // copy libs中的js文件到release文件夹 136 | gulp.task("copyLibsJsFile", ["copyFile"], function () { 137 | if (!config.onlyIndexJS) { 138 | return; 139 | } 140 | if (platform === "wxgame" && isOpendataProj) { // 开放域项目微信发布,拷贝文件时已经拷贝类库文件了 141 | return; 142 | } 143 | // 开放域项目,as语言,没有libs目录,mac系统报错 144 | let libs = path.join(workSpaceDir, "bin", "libs"); 145 | if (!fs.existsSync(libs)) { 146 | return; 147 | } 148 | // 分析index.js 149 | let indexJSPath = path.join(workSpaceDir, "bin", "index.js"); 150 | let indexJsContent = fs.readFileSync(indexJSPath, "utf8"); 151 | let libsList = indexJsContent.match(/loadLib\(['"]libs\/[a-zA-z0-9\/\.]+\.(js|wasm)['"]\)/g); 152 | if (!libsList) { 153 | libsList = []; 154 | } 155 | let 156 | item, 157 | libsName = "", 158 | libsStr = ""; 159 | for (let i = 0, len = libsList.length; i < len; i++) { 160 | item = libsList[i]; 161 | libsName = item.match(/loadLib\(['"]libs\/([a-zA-z0-9\/\.]+\.(js|wasm))['"]\)/); 162 | libsStr += libsStr ? `,${libsName[1]}` : libsName[1]; 163 | } 164 | let copyLibsList = [`${workSpaceDir}/bin/libs/{${libsStr}}`]; 165 | if (!libsStr.includes(",")) { 166 | copyLibsList = [`${workSpaceDir}/bin/libs/${libsStr}`]; 167 | } 168 | // 微信、百度,需要拷贝对应平台的类库 169 | if (platform === "wxgame") { 170 | copyLibsList.push(`${workSpaceDir}/bin/libs/laya.wxmini.js`); 171 | } else if (platform === "bdgame") { 172 | copyLibsList.push(`${workSpaceDir}/bin/libs/laya.bdmini.js`); 173 | } 174 | var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` }); 175 | return stream.pipe(gulp.dest(releaseDir)); 176 | }); 177 | 178 | // 根据不同的项目类型拷贝平台文件 179 | gulp.task("copyPlatformFile", ["copyLibsJsFile"], function () { 180 | let fileLibsPath; 181 | if (useIDENode) { 182 | fileLibsPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data"); 183 | } else if (process.argv.length >= 8 && process.argv[6] === "--libspath") { 184 | fileLibsPath = process.argv[7]; 185 | console.log("平台文件包是否存在: " + fs.existsSync(fileLibsPath)); 186 | } else { 187 | console.log("没有接收到可用文件包位置,不拷贝对应平台文件"); 188 | return; 189 | } 190 | // 开放域项目,微信发布 191 | if (platform === "wxgame" && isOpendataProj) { 192 | let platformDir = path.join(fileLibsPath, "wxfiles", "weapp-adapter.js"); 193 | let stream = gulp.src(platformDir); 194 | return stream.pipe(gulp.dest(releaseDir)); 195 | } 196 | // 微信项目,非开放域项目 197 | if (platform === "wxgame") { 198 | // 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝 199 | let isHadWXFiles = 200 | fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) && 201 | fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) && 202 | fs.existsSync(path.join(workSpaceDir, "bin", "project.config.json")) && 203 | fs.existsSync(path.join(workSpaceDir, "bin", "weapp-adapter.js")); 204 | if (isHadWXFiles) { 205 | return; 206 | } 207 | let platformDir = path.join(fileLibsPath, "wxfiles"); 208 | let stream = gulp.src(platformDir + "/*.*"); 209 | return stream.pipe(gulp.dest(releaseDir)); 210 | } 211 | // qq玩一玩项目,区分语言 212 | if (platform === "qqwanyiwan") { 213 | let projectLangDir = config.projectType; 214 | let platformDir = path.join(fileLibsPath, "qqfiles", projectLangDir); 215 | let stream = gulp.src(platformDir + "/**/*.*"); 216 | return stream.pipe(gulp.dest(releaseDir)); 217 | } 218 | // 百度项目 219 | if (platform === "bdgame") { 220 | // 如果新建项目时已经点击了"微信/百度小游戏bin目录快速调试",不再拷贝 221 | let isHadBdFiles = 222 | fs.existsSync(path.join(workSpaceDir, "bin", "game.js")) && 223 | fs.existsSync(path.join(workSpaceDir, "bin", "game.json")) && 224 | fs.existsSync(path.join(workSpaceDir, "bin", "project.swan.json")) && 225 | fs.existsSync(path.join(workSpaceDir, "bin", "swan-game-adapter.js")); 226 | if (isHadBdFiles) { 227 | return; 228 | } 229 | let platformDir = path.join(fileLibsPath, "bdfiles"); 230 | let stream = gulp.src(platformDir + "/*.*"); 231 | return stream.pipe(gulp.dest(releaseDir)); 232 | } 233 | }); 234 | 235 | // 拷贝文件后,针对特定平台修改文件内容 236 | gulp.task("modifyFile", ["copyPlatformFile"], function () { 237 | // QQ玩一玩 238 | if (platform === "qqwanyiwan") { 239 | // 修改bundle.js 240 | let bundleFilePath = path.join(releaseDir, "js", "bundle.js"); 241 | if (fs.existsSync(bundleFilePath)) { 242 | let fileContent = fs.readFileSync(bundleFilePath, "utf8"); 243 | var appendCode = 'if(window["BK"]&&window["BK"]["Sprite"]){BK.Script.loadlib("GameRes://layaforqq/laya.bkadpter.js");}'; 244 | if (fileContent.includes("/**LayaGameStart**/") && !fileContent.includes(appendCode)) { 245 | fileContent = fileContent.split("/**LayaGameStart**/").join(`/**LayaGameStart**/\n${appendCode}`); 246 | fs.writeFileSync(bundleFilePath, fileContent, "utf8"); 247 | } 248 | } 249 | 250 | // 修改index.js 251 | let indexFilePath = path.join(releaseDir, "index.js"); 252 | if (fs.existsSync(indexFilePath)) { 253 | let fileContent = fs.readFileSync(indexFilePath, "utf8"); 254 | fileContent = fileContent.replace(/loadLib\("/g, "BK.Script.loadlib(\"GameRes://"); 255 | // 非as语言,要添加类库 256 | if ("as" !== config.projectType) { 257 | if (fileContent.includes("//-----libs-end-------")) { 258 | fileContent = fileContent.split("//-----libs-end-------").join(`//-----libs-end-------\nBK.Script.loadlib("GameRes://layaforqq/laya.bkadpter.js");`); 259 | } 260 | } 261 | fs.writeFileSync(indexFilePath, fileContent, "utf8"); 262 | } 263 | 264 | // 修改main.js 265 | let mainFilePath = path.join(releaseDir, "main.js"); 266 | if (fs.existsSync(mainFilePath)) { 267 | let fileContent = fs.readFileSync(mainFilePath, "utf8"); 268 | fileContent = `BK.Script.loadlib("GameRes://layaforqq/qqPlayCore.js"); 269 | BK.Script.loadlib("GameRes://layaforqq/bkadptpre.js"); 270 | BK.Script.loadlib("GameRes://layaforqq/domparserinone.js"); 271 | BK.Script.loadlib("GameRes://index.js");`; 272 | fs.writeFileSync(mainFilePath, fileContent, "utf8"); 273 | } 274 | 275 | return; 276 | } 277 | 278 | // 百度项目,修改index.js 279 | if (platform === "bdgame") { 280 | let filePath = path.join(releaseDir, "index.js"); 281 | if (!fs.existsSync(filePath)) { 282 | return; 283 | } 284 | let fileContent = fs.readFileSync(filePath, "utf8"); 285 | fileContent = fileContent.replace(/loadLib\(/g, "require("); 286 | fs.writeFileSync(filePath, fileContent, "utf8"); 287 | return; 288 | } 289 | }); 290 | 291 | // 压缩json 292 | gulp.task("compressJson", ["modifyFile"], function () { 293 | if (config.compressJson) { 294 | return gulp.src(config.compressJsonFilter, { base: releaseDir }) 295 | .pipe(jsonminify()) 296 | .pipe(gulp.dest(releaseDir)); 297 | } 298 | }); 299 | 300 | // 压缩js 301 | gulp.task("compressJs", ["compressJson"], function () { 302 | if (config.compressJs) { 303 | return gulp.src(config.compressJsFilter, { base: releaseDir }) 304 | .pipe(uglify()) 305 | .on('error', function (err) { 306 | console.warn(err.toString()); 307 | }) 308 | .pipe(gulp.dest(releaseDir)); 309 | } 310 | }); 311 | 312 | // 压缩png,jpg 313 | gulp.task("compressImage", ["compressJs"], function () { 314 | if (config.compressImage) { 315 | return gulp.src(config.compressImageFilter, { base: releaseDir }) 316 | .pipe(image({ 317 | pngquant: true, //PNG优化工具 318 | optipng: false, //PNG优化工具 319 | zopflipng: true, //PNG优化工具 320 | jpegRecompress: false, //jpg优化工具 321 | mozjpeg: true, //jpg优化工具 322 | guetzli: false, //jpg优化工具 323 | gifsicle: false, //gif优化工具 324 | svgo: false, //SVG优化工具 325 | concurrent: 10, //并发线程数 326 | quiet: true //是否是静默方式 327 | // optipng: ['-i 1', '-strip all', '-fix', '-o7', '-force'], 328 | // pngquant: ['--speed=1', '--force', 256], 329 | // zopflipng: ['-y', '--lossy_8bit', '--lossy_transparent'], 330 | // jpegRecompress: ['--strip', '--quality', 'medium', '--min', 40, '--max', 80], 331 | // mozjpeg: ['-optimize', '-progressive'], 332 | // guetzli: ['--quality', 85] 333 | })) 334 | .pipe(gulp.dest(releaseDir)); 335 | } 336 | }); 337 | 338 | // 开放域的情况下,合并game.js和index.js,并删除game.js 339 | gulp.task("openData", ["compressImage"], function (cb) { 340 | if (config.openDataZone) { 341 | let indexPath = releaseDir + "/index.js"; 342 | let indexjs = readFile(indexPath); 343 | let gamejs = readFile(releaseDir + "/game.js"); 344 | if (gamejs && indexjs) { 345 | gamejs = gamejs.replace('require("index.js")', indexjs); 346 | fs.writeFileSync(indexPath, gamejs, 'utf-8'); 347 | } 348 | if (isOpendataProj) { 349 | // 开放域项目,将game.js删掉,发布最小包 350 | del(`${releaseDir}/game.js`, { force: true }).then(paths => { 351 | cb(); 352 | }); 353 | } else { 354 | cb(); 355 | } 356 | } else { 357 | cb(); 358 | } 359 | }); 360 | 361 | function readFile(path) { 362 | if (fs.existsSync(path)) { 363 | return fs.readFileSync(path, "utf-8"); 364 | } 365 | return null; 366 | } 367 | 368 | // 生成版本管理信息 369 | gulp.task("version1", ["openData"], function () { 370 | if (config.version) { 371 | return gulp.src(config.versionFilter, { base: releaseDir }) 372 | .pipe(rev()) 373 | .pipe(gulp.dest(releaseDir)) 374 | .pipe(revdel()) 375 | .pipe(rev.manifest("version.json")) 376 | .pipe(gulp.dest(releaseDir)); 377 | } 378 | }); 379 | 380 | // 替换index.js里面的变化的文件名 381 | gulp.task("version2", ["version1"], function () { 382 | if (config.version) { 383 | //替换index.html和index.js里面的文件名称 384 | 385 | let htmlPath = releaseDir + "/index.html"; 386 | let versionPath = releaseDir + "/version.json"; 387 | let gameJSPath = releaseDir + "/game.js"; 388 | let mainJSPath = releaseDir + "/main.js"; 389 | let indexJSPath; 390 | let versionCon = fs.readFileSync(versionPath, "utf8"); 391 | versionCon = JSON.parse(versionCon); 392 | indexJSPath = releaseDir + "/" + versionCon["index.js"]; 393 | // 替换config.packfileFullValue中的路径 394 | let packfileStr = JSON.stringify(config.packfileFullValue).replace(/\\\\/g, "/"); 395 | let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`; 396 | fs.writeFileSync(tempPackfile, packfileStr, "utf8"); 397 | 398 | let srcList = [versionPath, indexJSPath, tempPackfile]; 399 | if (fs.existsSync(htmlPath)) { 400 | srcList.push(htmlPath); 401 | } 402 | if (fs.existsSync(gameJSPath)) { 403 | srcList.push(gameJSPath); 404 | } 405 | if (fs.existsSync(mainJSPath)) { 406 | srcList.push(mainJSPath); 407 | } 408 | return gulp.src(srcList) 409 | .pipe(revCollector()) 410 | .pipe(gulp.dest(releaseDir)); 411 | } 412 | }); 413 | 414 | // 筛选4M包 415 | gulp.task("packfile", ["version2"], function() { 416 | if (config.version) { 417 | // 从release目录取得带有版本号的目录 418 | let tempPackfile = `${workSpaceDir}/.laya/configTemp.json`; 419 | let releasePackfile = `${releaseDir}/configTemp.json`; 420 | let packfileStr = fs.readFileSync(releasePackfile, "utf8"); 421 | config.packfileFullValue = JSON.parse(packfileStr); 422 | // 删掉临时目录 423 | fs.unlinkSync(tempPackfile); 424 | fs.unlinkSync(releasePackfile); 425 | } 426 | if (config.packfile) { // 提取本地包(文件列表形式) 427 | return gulp.src(config.packfileFullValue, { base: releaseDir }) 428 | .pipe(gulp.dest(config.packfileTargetValue || releaseDir + "_pack")); 429 | } 430 | }); 431 | 432 | // 起始任务 433 | gulp.task("publish", ["buildXiaomiProj", "buildOPPOProj"], function () { 434 | console.log("All tasks completed!"); 435 | }); -------------------------------------------------------------------------------- /.laya/publish_oppogame.js: -------------------------------------------------------------------------------- 1 | // v1.0.1 2 | // publish 2.x 也是用这个文件,需要做兼容 3 | let isPublish2 = process.argv[2].includes("publish_oppogame.js") && process.argv[3].includes("--evn=publish2"); 4 | // 获取Node插件和工作路径 5 | let ideModuleDir, workSpaceDir; 6 | if (isPublish2) { 7 | //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行) 8 | const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false; 9 | ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : ""; 10 | workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_oppogame.js", "").replace("/.laya/publish_oppogame.js", "") + "/" : "./../"; 11 | } else { 12 | ideModuleDir = global.ideModuleDir; 13 | workSpaceDir = global.workSpaceDir; 14 | } 15 | 16 | //引用插件模块 17 | const gulp = require(ideModuleDir + "gulp"); 18 | const fs = require("fs"); 19 | const path = require("path"); 20 | const childProcess = require("child_process"); 21 | const del = require(ideModuleDir + "del"); 22 | let commandSuffix = ".cmd"; 23 | 24 | let prevTasks = ["packfile"]; 25 | if (isPublish2) { 26 | prevTasks = ""; 27 | } 28 | 29 | let 30 | config, 31 | platform, 32 | releaseDir, 33 | toolkitPath, 34 | tempReleaseDir, // OPPO临时拷贝目录 35 | projDir; // OPPO快游戏工程目录 36 | // 创建OPPO项目前,拷贝OPPO引擎库、修改index.js 37 | // 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改 38 | gulp.task("preCreate_OPPO", prevTasks, function() { 39 | if (isPublish2) { 40 | let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json"); 41 | let content = fs.readFileSync(pubsetPath, "utf8"); 42 | let pubsetJson = JSON.parse(content); 43 | platform = "oppogame"; 44 | releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/"); 45 | releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease"); 46 | config = pubsetJson[5]; // 只用到了 config.oppoInfo|config.oppoSign 47 | } else { 48 | platform = global.platform; 49 | releaseDir = global.releaseDir; 50 | tempReleaseDir = global.tempReleaseDir; 51 | config = global.config; 52 | } 53 | toolkitPath = path.join(ideModuleDir, "../", "out", "layarepublic", "oppo", "quickgame-toolkit"); 54 | // 如果不是OPPO快游戏 55 | if (platform !== "oppogame") { 56 | return; 57 | } 58 | if (process.platform === "darwin") { 59 | commandSuffix = ""; 60 | } 61 | let copyLibsList = [`${workSpaceDir}/bin/libs/laya.quickgamemini.js`]; 62 | var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` }); 63 | return stream.pipe(gulp.dest(tempReleaseDir)); 64 | }); 65 | 66 | // 新建OPPO项目-OPPO项目与其他项目不同,需要安装OPPO quickgame node_modules,并打包成.rpk文件 67 | gulp.task("installModules_OPPO", ["preCreate_OPPO"], function() { 68 | // 如果不是OPPO快游戏 69 | if (platform !== "oppogame") { 70 | return; 71 | } 72 | releaseDir = path.dirname(releaseDir); 73 | projDir = path.join(releaseDir, config.oppoInfo.projName); 74 | // 如果IDE里对应OPPO包已经install node_modules了,忽略这一步 75 | if (fs.existsSync(path.join(toolkitPath, "node_modules"))) { 76 | return; 77 | } 78 | // 安装OPPO quickgame node_modules 79 | return new Promise((resolve, reject) => { 80 | console.log("开始安装OPPO quickgame node_modules,请耐心等待..."); 81 | let cmd = `npm${commandSuffix}`; 82 | let args = ["install"]; 83 | 84 | let cp = childProcess.spawn(cmd, args, { 85 | cwd: toolkitPath 86 | }); 87 | 88 | cp.stdout.on('data', (data) => { 89 | console.log(`stdout: ${data}`); 90 | }); 91 | 92 | cp.stderr.on('data', (data) => { 93 | console.log(`stderr: ${data}`); 94 | // reject(); 95 | }); 96 | 97 | cp.on('close', (code) => { 98 | console.log(`子进程退出码:${code}`); 99 | resolve(); 100 | }); 101 | }); 102 | }); 103 | 104 | // 拷贝文件到OPPO快游戏 105 | gulp.task("copyFileToProj_OPPO", ["installModules_OPPO"], function() { 106 | // 如果不是OPPO快游戏 107 | if (platform !== "oppogame") { 108 | return; 109 | } 110 | // 将临时文件夹中的文件,拷贝到项目中去 111 | let originalDir = `${tempReleaseDir}/**/*.*`; 112 | let stream = gulp.src(originalDir); 113 | return stream.pipe(gulp.dest(path.join(projDir))); 114 | }); 115 | 116 | // 拷贝icon到OPPO快游戏 117 | gulp.task("copyIconToProj_OPPO", ["copyFileToProj_OPPO"], function() { 118 | // 如果不是OPPO快游戏 119 | if (platform !== "oppogame") { 120 | return; 121 | } 122 | let originalDir = config.oppoInfo.icon; 123 | let stream = gulp.src(originalDir); 124 | return stream.pipe(gulp.dest(path.join(projDir))); 125 | }); 126 | 127 | // 清除OPPO快游戏临时目录 128 | gulp.task("clearTempDir_OPPO", ["copyIconToProj_OPPO"], function() { 129 | // 如果不是OPPO快游戏 130 | if (platform !== "oppogame") { 131 | return; 132 | } 133 | // 删掉临时目录 134 | return del([tempReleaseDir], { force: true }); 135 | }); 136 | 137 | // 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem ) 138 | gulp.task("generateSign_OPPO", ["clearTempDir_OPPO"], function() { 139 | // 如果不是OPPO快游戏 140 | if (platform !== "oppogame") { 141 | return; 142 | } 143 | if (!config.oppoSign.generateSign) { 144 | return; 145 | } 146 | // https://doc.quickapp.cn/tools/compiling-tools.html 147 | return new Promise((resolve, reject) => { 148 | let cmd = "openssl"; 149 | let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 150 | "-x509", "-days", "3650", "-out", "certificate.pem"]; 151 | let opts = { 152 | cwd: projDir, 153 | shell: true 154 | }; 155 | let cp = childProcess.spawn(cmd, args, opts); 156 | cp.stdout.on('data', (data) => { 157 | console.log(`stdout: ${data}`); 158 | }); 159 | 160 | cp.stderr.on('data', (data) => { 161 | console.log(`stderr: ${data}`); 162 | data += ""; 163 | if (data.includes("Country Name")) { 164 | cp.stdin.write(`${config.oppoSign.countryName}\n`); 165 | console.log(`Country Name: ${config.oppoSign.countryName}`); 166 | } else if (data.includes("Province Name")) { 167 | cp.stdin.write(`${config.oppoSign.provinceName}\n`); 168 | console.log(`Province Name: ${config.oppoSign.provinceName}`); 169 | } else if (data.includes("Locality Name")) { 170 | cp.stdin.write(`${config.oppoSign.localityName}\n`); 171 | console.log(`Locality Name: ${config.oppoSign.localityName}`); 172 | } else if (data.includes("Organization Name")) { 173 | cp.stdin.write(`${config.oppoSign.orgName}\n`); 174 | console.log(`Organization Name: ${config.oppoSign.orgName}`); 175 | } else if (data.includes("Organizational Unit Name")) { 176 | cp.stdin.write(`${config.oppoSign.orgUnitName}\n`); 177 | console.log(`Organizational Unit Name: ${config.oppoSign.orgUnitName}`); 178 | } else if (data.includes("Common Name")) { 179 | cp.stdin.write(`${config.oppoSign.commonName}\n`); 180 | console.log(`Common Name: ${config.oppoSign.commonName}`); 181 | } else if (data.includes("Email Address")) { 182 | cp.stdin.write(`${config.oppoSign.emailAddr}\n`); 183 | console.log(`Email Address: ${config.oppoSign.emailAddr}`); 184 | // cp.stdin.end(); 185 | } 186 | // reject(); 187 | }); 188 | 189 | cp.on('close', (code) => { 190 | console.log(`子进程退出码:${code}`); 191 | resolve(); 192 | }); 193 | }); 194 | }); 195 | 196 | // 拷贝sign文件到指定位置 197 | gulp.task("copySignFile_OPPO", ["generateSign_OPPO"], function() { 198 | // 如果不是OPPO快游戏 199 | if (platform !== "oppogame") { 200 | return; 201 | } 202 | if (config.oppoSign.generateSign) { // 新生成的签名 203 | // 移动签名文件到项目中(Laya & OPPO快游戏项目中) 204 | let 205 | privatePem = path.join(projDir, "private.pem"), 206 | certificatePem = path.join(projDir, "certificate.pem"); 207 | let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem); 208 | if (!isSignExits) { 209 | return; 210 | } 211 | let 212 | xiaomiDest = `${projDir}/sign/release`, 213 | layaDest = `${workSpaceDir}/sign/release`; 214 | let stream = gulp.src([privatePem, certificatePem]); 215 | return stream.pipe(gulp.dest(xiaomiDest)) 216 | .pipe(gulp.dest(layaDest)); 217 | } else if (config.oppoInfo.useReleaseSign && !config.oppoSign.generateSign) { // 使用release签名,并且没有重新生成 218 | // 从项目中将签名拷贝到OPPO快游戏项目中 219 | let 220 | privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"), 221 | certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem"); 222 | let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem); 223 | if (!isSignExits) { 224 | return; 225 | } 226 | let 227 | xiaomiDest = `${projDir}/sign/release`; 228 | let stream = gulp.src([privatePem, certificatePem]); 229 | return stream.pipe(gulp.dest(xiaomiDest)); 230 | } 231 | }); 232 | 233 | gulp.task("deleteSignFile_OPPO", ["copySignFile_OPPO"], function() { 234 | // 如果不是OPPO快游戏 235 | if (platform !== "oppogame") { 236 | return; 237 | } 238 | if (config.oppoSign.generateSign) { // 新生成的签名 239 | let 240 | privatePem = path.join(projDir, "private.pem"), 241 | certificatePem = path.join(projDir, "certificate.pem"); 242 | return del([privatePem, certificatePem], { force: true }); 243 | } 244 | }); 245 | 246 | gulp.task("modifyFile_OPPO", ["deleteSignFile_OPPO"], function() { 247 | // 如果不是OPPO快游戏 248 | if (platform !== "oppogame") { 249 | return; 250 | } 251 | // 修改manifest.json文件 252 | let manifestPath = path.join(projDir, "manifest.json"); 253 | let IDEManifestPath = path.join(toolkitPath, "tpl", "manifest.json"); 254 | if (!fs.existsSync(IDEManifestPath)) { 255 | return; 256 | } 257 | let manifestContent = fs.readFileSync(IDEManifestPath, "utf8"); 258 | let manifestJson = JSON.parse(manifestContent); 259 | manifestJson.package = config.oppoInfo.package; 260 | manifestJson.name = config.oppoInfo.name; 261 | manifestJson.orientation = config.oppoInfo.orientation; 262 | manifestJson.versionName = config.oppoInfo.versionName; 263 | manifestJson.versionCode = config.oppoInfo.versionCode; 264 | manifestJson.minPlatformVersion = config.oppoInfo.minPlatformVersion; 265 | manifestJson.icon = `./${path.basename(config.oppoInfo.icon)}`; 266 | if (config.oppoInfo.subpack) { 267 | manifestJson.subpackages = config.oppoSubpack; 268 | } 269 | fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8"); 270 | 271 | // OPPO项目,修改main.js 272 | let filePath = path.join(projDir, "main.js"); 273 | // 这个地方,1.x IDE和2.x IDE 不一致 274 | let fileContent = `window.navigator.userAgent = 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 OPPO MiniGame NetType/WIFI Language/zh_CN'; 275 | require("./libs/laya.quickgamemini.js");\nrequire("index.js");`; 276 | fs.writeFileSync(filePath, fileContent, "utf8"); 277 | 278 | // OPPO项目,修改index.js 279 | let indexFilePath = path.join(projDir, "index.js"); 280 | if (!fs.existsSync(indexFilePath)) { 281 | return; 282 | } 283 | let indexFileContent = fs.readFileSync(indexFilePath, "utf8"); 284 | indexFileContent = indexFileContent.replace(/loadLib(\(['"])/gm, "require$1./"); 285 | fs.writeFileSync(indexFilePath, indexFileContent, "utf8"); 286 | }); 287 | 288 | // 打包rpk 289 | gulp.task("buildRPK_OPPO", ["modifyFile_OPPO"], function() { 290 | // 如果不是OPPO快游戏 291 | if (platform !== "oppogame") { 292 | return; 293 | } 294 | // 在OPPO轻游戏项目目录中执行: 295 | // quickgame pack || quickgame pack release 296 | // quickgame subpack --no-build-js || quickgame subpack release --no-build-js 297 | let cmdStr = ""; 298 | let packStr = "pack"; 299 | let nobuildjs = ""; 300 | if (config.oppoInfo.subpack) { 301 | packStr = "subpack"; 302 | nobuildjs = "--no-build-js"; 303 | } 304 | if (config.oppoInfo.useReleaseSign) { 305 | cmdStr = "release"; 306 | } 307 | return new Promise((resolve, reject) => { 308 | let cmd = path.join(toolkitPath, "lib", "bin", `quickgame${commandSuffix}`); 309 | let args = [packStr, cmdStr, nobuildjs]; 310 | let opts = { 311 | cwd: projDir 312 | }; 313 | let cp = childProcess.spawn(cmd, args, opts); 314 | // let cp = childProcess.spawn('npx.cmd', ['-v']); 315 | cp.stdout.on('data', (data) => { 316 | console.log(`stdout: ${data}`); 317 | }); 318 | 319 | cp.stderr.on('data', (data) => { 320 | console.log(`stderr: ${data}`); 321 | // reject(); 322 | }); 323 | 324 | cp.on('close', (code) => { 325 | console.log(`子进程退出码:${code}`); 326 | resolve(); 327 | }); 328 | }); 329 | }); 330 | 331 | gulp.task("pushRPK_OPPO", ["buildRPK_OPPO"], function() { 332 | // 如果不是OPPO快游戏 333 | if (platform !== "oppogame") { 334 | return; 335 | } 336 | if (!config.oppoInfo.oppoDebug) { 337 | return; 338 | } 339 | // 在OPPO轻游戏项目目录中执行: 340 | // adb push dist/game.rpk sdcard/games 341 | // adb push idePath/resources/app/out/layarepublic/oppo/instant_app_settings.properties 342 | // adb shell am start -n com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity 343 | return new Promise((resolve, reject) => { 344 | let cmd = "adb"; 345 | let sdGamesPath = config.oppoInfo.subpack ? "sdcard/subPkg" : "sdcard/games"; 346 | let args = ["push", `dist/${config.oppoInfo.package}${config.oppoInfo.useReleaseSign ? ".signed" : ""}.rpk`, sdGamesPath]; 347 | let opts = { 348 | cwd: projDir 349 | }; 350 | let cp = childProcess.spawn(cmd, args, opts); 351 | // let cp = childProcess.spawn('npx.cmd', ['-v']); 352 | cp.stdout.on('data', (data) => { 353 | console.log(`stdout: ${data}`); 354 | }); 355 | 356 | cp.stderr.on('data', (data) => { 357 | console.log(`stderr: ${data}`); 358 | // reject(); 359 | }); 360 | 361 | cp.on('close', (code) => { 362 | console.log(`1) push_RPK 子进程退出码:${code}`); 363 | resolve(); 364 | }); 365 | }).then(() => { 366 | return new Promise((resolve, reject) => { 367 | // 如果是分包,需要修改里面的内容 368 | let oppoPropPath = path.join(ideModuleDir, "../", `/out/layarepublic/oppo/instant_app_settings.properties`); 369 | if (config.oppoInfo.subpack) { 370 | fs.writeFileSync(oppoPropPath, "default_tab_index=4", "utf8"); 371 | } else { 372 | fs.writeFileSync(oppoPropPath, "default_tab_index=2", "utf8"); 373 | } 374 | let cmd = "adb"; 375 | let args = ["push", oppoPropPath, "sdcard/"]; 376 | let opts = { 377 | cwd: projDir 378 | }; 379 | let cp = childProcess.spawn(cmd, args, opts); 380 | // let cp = childProcess.spawn('npx.cmd', ['-v']); 381 | cp.stdout.on('data', (data) => { 382 | console.log(`stdout: ${data}`); 383 | }); 384 | 385 | cp.stderr.on('data', (data) => { 386 | console.log(`stderr: ${data}`); 387 | // reject(); 388 | }); 389 | 390 | cp.on('close', (code) => { 391 | console.log(`2) push_RPK 子进程退出码:${code}`); 392 | resolve(); 393 | }); 394 | }); 395 | }).then(() => { 396 | return new Promise((resolve, reject) => { 397 | let cmd = "adb"; 398 | let args = ["shell", "am", "start", "-n", "com.nearme.instant.platform/com.oppo.autotest.main.InstantAppActivity"]; 399 | let opts = { 400 | cwd: projDir 401 | }; 402 | let cp = childProcess.spawn(cmd, args, opts); 403 | // let cp = childProcess.spawn('npx.cmd', ['-v']); 404 | cp.stdout.on('data', (data) => { 405 | console.log(`stdout: ${data}`); 406 | }); 407 | 408 | cp.stderr.on('data', (data) => { 409 | console.log(`stderr: ${data}`); 410 | // reject(); 411 | }); 412 | 413 | cp.on('close', (code) => { 414 | console.log(`3) push_RPK 子进程退出码:${code}`); 415 | resolve(); 416 | }); 417 | }); 418 | }); 419 | }); 420 | 421 | gulp.task("buildOPPOProj", ["pushRPK_OPPO"], function() { 422 | console.log("all tasks completed"); 423 | }); -------------------------------------------------------------------------------- /.laya/publish_xmgame.js: -------------------------------------------------------------------------------- 1 | // v1.1.0 2 | // publish 2.x 也是用这个文件,需要做兼容 3 | let isPublish2 = process.argv[2].includes("publish_xmgame.js") && process.argv[3].includes("--evn=publish2"); 4 | // 获取Node插件和工作路径 5 | let ideModuleDir, workSpaceDir; 6 | if (isPublish2) { 7 | //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行) 8 | const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false; 9 | ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : ""; 10 | workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_xmgame.js", "").replace("/.laya/publish_xmgame.js", "") + "/" : "./../"; 11 | } else { 12 | ideModuleDir = global.ideModuleDir; 13 | workSpaceDir = global.workSpaceDir; 14 | } 15 | 16 | //引用插件模块 17 | const gulp = require(ideModuleDir + "gulp"); 18 | const fs = require("fs"); 19 | const path = require("path"); 20 | const childProcess = require("child_process"); 21 | const del = require(ideModuleDir + "del"); 22 | let commandSuffix = ".cmd"; 23 | 24 | let prevTasks = ["packfile"]; 25 | if (isPublish2) { 26 | prevTasks = ""; 27 | } 28 | 29 | let 30 | config, 31 | platform, 32 | releaseDir, 33 | tempReleaseDir, // 小米临时拷贝目录 34 | projDir; // 小米快游戏工程目录 35 | let IDEXMProjPath, 36 | isUpdateIDEXMProj = false; 37 | // 创建小米项目前,拷贝小米引擎库、修改index.js 38 | // 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改 39 | gulp.task("preCreate_XM", prevTasks, function() { 40 | if (isPublish2) { 41 | let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json"); 42 | let content = fs.readFileSync(pubsetPath, "utf8"); 43 | let pubsetJson = JSON.parse(content); 44 | platform = "xmgame"; 45 | releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/"); 46 | releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease"); 47 | config = pubsetJson[4]; // 只用到了 config.xmInfo|config.xmSign 48 | } else { 49 | platform = global.platform; 50 | releaseDir = global.releaseDir; 51 | tempReleaseDir = global.tempReleaseDir; 52 | config = global.config; 53 | } 54 | // 如果不是小米快游戏 55 | if (platform !== "xmgame") { 56 | return; 57 | } 58 | if (process.platform === "darwin") { 59 | commandSuffix = ""; 60 | } 61 | let copyLibsList = [`${workSpaceDir}/bin/libs/laya.xmmini.js`]; 62 | var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` }); 63 | return stream.pipe(gulp.dest(tempReleaseDir)); 64 | }); 65 | 66 | gulp.task("copyPlatformFile_XM", ["preCreate_XM"], function() { 67 | // 如果不是小米快游戏 68 | if (platform !== "xmgame") { 69 | return; 70 | } 71 | let xmAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "xmfiles"); 72 | let copyLibsList = [`${xmAdapterPath}/**/*.*`]; 73 | var stream = gulp.src(copyLibsList); 74 | return stream.pipe(gulp.dest(tempReleaseDir)); 75 | }); 76 | 77 | // 新建小米项目-小米项目与其他项目不同,需要新建小米快游戏项目,并打包成.rpk文件 78 | gulp.task("checkIDEProj_XM", ["copyPlatformFile_XM"], function() { 79 | // 如果不是小米快游戏 80 | if (platform !== "xmgame") { 81 | return; 82 | } 83 | if (!ideModuleDir) { 84 | return; 85 | } 86 | IDEXMProjPath = path.join(ideModuleDir, "../", "out", "layarepublic", "xm"); 87 | if (process.platform === "darwin") { 88 | return; 89 | } 90 | let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName); 91 | // 如果IDE中没有小米项目,跳过这一步 92 | let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 93 | fs.existsSync(ideLastXMProjPath + "/sign"); 94 | if (!isProjExist) { 95 | console.log("IDE中没有小米项目,跳过检查小米项目版本号这一步"); 96 | return; 97 | } 98 | // 如果IDE中项目已经存在了,检查版本号 99 | // npm view quickgame-cli version 100 | // npm ls quickgame-cli 101 | let remoteVersion, localVersion; 102 | let isGetRemote, isGetLocal; 103 | return new Promise((resolve, reject) => { // 远程版本号 104 | childProcess.exec("npm view quickgame-cli version", function(error, stdout, stderr) { 105 | if (!stdout) { // 获取 quickgame-cli 远程版本号失败 106 | reject(); 107 | return; 108 | } 109 | remoteVersion = stdout; 110 | isGetRemote = true; 111 | if (isGetRemote && isGetLocal) { 112 | resolve(); 113 | } 114 | }); 115 | childProcess.exec("npm ls quickgame-cli", { cwd: ideLastXMProjPath }, function(error, stdout, stderr) { 116 | if (!stdout) { // 获取 quickgame-cli 本地版本号失败 117 | reject(); 118 | return; 119 | } 120 | localVersion = stdout.match(/quickgame-cli@(.+)/); 121 | localVersion = localVersion && localVersion[1]; 122 | isGetLocal = true; 123 | if (isGetRemote && isGetLocal) { 124 | resolve(); 125 | } 126 | }); 127 | setTimeout(() => { 128 | if (!isGetLocal || !isGetRemote) { 129 | console.log("获取远程版本号或本地版本号失败"); 130 | reject(); 131 | return; 132 | } 133 | }, 10000); 134 | }).then(() => { // 比较两个版本号 135 | if (!remoteVersion || !localVersion) { 136 | console.log("获取远程版本号或本地版本号失败!"); 137 | } 138 | console.log("quickgame-cli -> ", localVersion, "|", remoteVersion); 139 | if (remoteVersion.trim() !== localVersion.trim()) { // 仅当两个版本号都获取到并且不相等,置为需要更新(true) 140 | isUpdateIDEXMProj = true; 141 | } 142 | }).catch((e) => { 143 | console.log("获取远程版本号或本地版本号失败 -> ", remoteVersion, "|", localVersion); 144 | console.log(e); 145 | }); 146 | }); 147 | 148 | gulp.task("createIDEProj_XM", ["checkIDEProj_XM"], function() { 149 | // 如果不是小米快游戏 150 | if (platform !== "xmgame") { 151 | return; 152 | } 153 | if (!ideModuleDir) { 154 | return; 155 | } 156 | if (process.platform === "darwin") { 157 | return; 158 | } 159 | let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName); 160 | // 如果有即存项目,不再新建 161 | let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 162 | fs.existsSync(ideLastXMProjPath + "/sign"); 163 | if (isProjExist && !isUpdateIDEXMProj) { // 项目存在并且不需要更新IDE中的小米项目 164 | return; 165 | } 166 | return new Promise((resolve, reject) => { 167 | console.log("(IDE)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)..."); 168 | let cmd = `npx${commandSuffix}`; 169 | let args = ["create-quickgame", config.xmInfo.projName, `path=${IDEXMProjPath}`, 170 | `package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`, 171 | `versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`, 172 | `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`]; 173 | console.log(JSON.stringify(args)); 174 | 175 | let cp = childProcess.spawn(cmd, args); 176 | 177 | cp.stdout.on('data', (data) => { 178 | console.log(`stdout: ${data}`); 179 | }); 180 | 181 | cp.stderr.on('data', (data) => { 182 | console.log(`stderr: ${data}`); 183 | // reject(); 184 | }); 185 | 186 | cp.on('close', (code) => { 187 | console.log(`子进程退出码:${code}`); 188 | resolve(); 189 | }); 190 | }); 191 | }); 192 | 193 | gulp.task("createProj_XM", ["createIDEProj_XM"], function() { 194 | // 如果不是小米快游戏 195 | if (platform !== "xmgame") { 196 | return; 197 | } 198 | releaseDir = path.dirname(releaseDir); 199 | projDir = path.join(releaseDir, config.xmInfo.projName); 200 | // 如果有即存项目,不再新建 201 | let isProjExist = fs.existsSync(projDir + "/node_modules") && 202 | fs.existsSync(projDir + "/sign"); 203 | if (isProjExist) { 204 | return; 205 | } 206 | // 如果IDE中有即存项目,不再新建,从IDE中拷贝 207 | let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName); 208 | let isIDEXMProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") && 209 | fs.existsSync(ideLastXMProjPath + "/sign"); 210 | if (isIDEXMProjExist) { // 如果用的IDE并且有IDEXM目录 211 | console.log("使用IDE中的小米游戏项目,拷贝..."); 212 | // node-glob语法中,* 无法匹配 .开头的文件(夹),必须手动匹配 213 | let IDEXMProjPathStr = [`${IDEXMProjPath}/**/*.*`, `${ideLastXMProjPath}/node_modules/.bin/*.*`]; 214 | var stream = gulp.src(IDEXMProjPathStr, { base: IDEXMProjPath}); 215 | return stream.pipe(gulp.dest(releaseDir)); 216 | } 217 | // 在项目中创建小米项目 218 | return new Promise((resolve, reject) => { 219 | console.log("(proj)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)..."); 220 | let cmd = `npx${commandSuffix}`; 221 | let args = ["create-quickgame", config.xmInfo.projName, `path=${releaseDir}`, 222 | `package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`, 223 | `versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`, 224 | `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`]; 225 | console.log(JSON.stringify(args)); 226 | 227 | let cp = childProcess.spawn(cmd, args); 228 | 229 | cp.stdout.on('data', (data) => { 230 | console.log(`stdout: ${data}`); 231 | }); 232 | 233 | cp.stderr.on('data', (data) => { 234 | console.log(`stderr: ${data}`); 235 | // reject(); 236 | }); 237 | 238 | cp.on('close', (code) => { 239 | console.log(`子进程退出码:${code}`); 240 | resolve(); 241 | }); 242 | }); 243 | }); 244 | 245 | // 拷贝文件到小米快游戏 246 | gulp.task("copyFileToProj_XM", ["createProj_XM"], function() { 247 | // 如果不是小米快游戏 248 | if (platform !== "xmgame") { 249 | return; 250 | } 251 | // 将临时文件夹中的文件,拷贝到项目中去 252 | let originalDir = `${tempReleaseDir}/**/*.*`; 253 | let stream = gulp.src(originalDir); 254 | return stream.pipe(gulp.dest(path.join(projDir))); 255 | }); 256 | 257 | // 拷贝icon到小米快游戏 258 | gulp.task("copyIconToProj_XM", ["copyFileToProj_XM"], function() { 259 | // 如果不是小米快游戏 260 | if (platform !== "xmgame") { 261 | return; 262 | } 263 | let originalDir = config.xmInfo.icon; 264 | let stream = gulp.src(originalDir); 265 | return stream.pipe(gulp.dest(path.join(projDir, "layaicon"))); 266 | }); 267 | 268 | // 清除小米快游戏临时目录 269 | gulp.task("clearTempDir_XM", ["copyIconToProj_XM"], function() { 270 | // 如果不是小米快游戏 271 | if (platform !== "xmgame") { 272 | return; 273 | } 274 | // 删掉临时目录 275 | return del([tempReleaseDir], { force: true }); 276 | }); 277 | 278 | // 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem ) 279 | gulp.task("generateSign_XM", ["clearTempDir_XM"], function() { 280 | // 如果不是小米快游戏 281 | if (platform !== "xmgame") { 282 | return; 283 | } 284 | if (!config.xmSign.generateSign) { 285 | return; 286 | } 287 | // https://doc.quickapp.cn/tools/compiling-tools.html 288 | return new Promise((resolve, reject) => { 289 | let cmd = "openssl"; 290 | let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem", 291 | "-x509", "-days", "3650", "-out", "certificate.pem"]; 292 | let opts = { 293 | cwd: projDir, 294 | shell: true 295 | }; 296 | let cp = childProcess.spawn(cmd, args, opts); 297 | cp.stdout.on('data', (data) => { 298 | console.log(`stdout: ${data}`); 299 | }); 300 | 301 | cp.stderr.on('data', (data) => { 302 | console.log(`stderr: ${data}`); 303 | data += ""; 304 | if (data.includes("Country Name")) { 305 | cp.stdin.write(`${config.xmSign.countryName}\n`); 306 | console.log(`Country Name: ${config.xmSign.countryName}`); 307 | } else if (data.includes("Province Name")) { 308 | cp.stdin.write(`${config.xmSign.provinceName}\n`); 309 | console.log(`Province Name: ${config.xmSign.provinceName}`); 310 | } else if (data.includes("Locality Name")) { 311 | cp.stdin.write(`${config.xmSign.localityName}\n`); 312 | console.log(`Locality Name: ${config.xmSign.localityName}`); 313 | } else if (data.includes("Organization Name")) { 314 | cp.stdin.write(`${config.xmSign.orgName}\n`); 315 | console.log(`Organization Name: ${config.xmSign.orgName}`); 316 | } else if (data.includes("Organizational Unit Name")) { 317 | cp.stdin.write(`${config.xmSign.orgUnitName}\n`); 318 | console.log(`Organizational Unit Name: ${config.xmSign.orgUnitName}`); 319 | } else if (data.includes("Common Name")) { 320 | cp.stdin.write(`${config.xmSign.commonName}\n`); 321 | console.log(`Common Name: ${config.xmSign.commonName}`); 322 | } else if (data.includes("Email Address")) { 323 | cp.stdin.write(`${config.xmSign.emailAddr}\n`); 324 | console.log(`Email Address: ${config.xmSign.emailAddr}`); 325 | // cp.stdin.end(); 326 | } 327 | // reject(); 328 | }); 329 | 330 | cp.on('close', (code) => { 331 | console.log(`子进程退出码:${code}`); 332 | resolve(); 333 | }); 334 | }); 335 | }); 336 | 337 | // 拷贝sign文件到指定位置 338 | gulp.task("copySignFile_XM", ["generateSign_XM"], function() { 339 | // 如果不是小米快游戏 340 | if (platform !== "xmgame") { 341 | return; 342 | } 343 | if (config.xmSign.generateSign) { // 新生成的签名 344 | // 移动签名文件到项目中(Laya & 小米快游戏项目中) 345 | let 346 | privatePem = path.join(projDir, "private.pem"), 347 | certificatePem = path.join(projDir, "certificate.pem"); 348 | let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem); 349 | if (!isSignExits) { 350 | return; 351 | } 352 | let 353 | xiaomiDest = `${projDir}/sign/release`, 354 | layaDest = `${workSpaceDir}/sign/release`; 355 | let stream = gulp.src([privatePem, certificatePem]); 356 | return stream.pipe(gulp.dest(xiaomiDest)) 357 | .pipe(gulp.dest(layaDest)); 358 | } else if (config.xmInfo.useReleaseSign && !config.xmSign.generateSign) { // 使用release签名,并且没有重新生成 359 | // 从项目中将签名拷贝到小米快游戏项目中 360 | let 361 | privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"), 362 | certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem"); 363 | let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem); 364 | if (!isSignExits) { 365 | return; 366 | } 367 | let 368 | xiaomiDest = `${projDir}/sign/release`; 369 | let stream = gulp.src([privatePem, certificatePem]); 370 | return stream.pipe(gulp.dest(xiaomiDest)); 371 | } 372 | }); 373 | 374 | gulp.task("deleteSignFile_XM", ["copySignFile_XM"], function() { 375 | // 如果不是小米快游戏 376 | if (platform !== "xmgame") { 377 | return; 378 | } 379 | if (config.xmSign.generateSign) { // 新生成的签名 380 | let 381 | privatePem = path.join(projDir, "private.pem"), 382 | certificatePem = path.join(projDir, "certificate.pem"); 383 | return del([privatePem, certificatePem], { force: true }); 384 | } 385 | }); 386 | 387 | gulp.task("modifyFile_XM", ["deleteSignFile_XM"], function() { 388 | // 如果不是小米快游戏 389 | if (platform !== "xmgame") { 390 | return; 391 | } 392 | // 修改manifest.json文件 393 | let manifestPath = path.join(projDir, "manifest.json"); 394 | if (!fs.existsSync(manifestPath)) { 395 | return; 396 | } 397 | let manifestContent = fs.readFileSync(manifestPath, "utf8"); 398 | let manifestJson = JSON.parse(manifestContent); 399 | manifestJson.package = config.xmInfo.package; 400 | manifestJson.name = config.xmInfo.name; 401 | manifestJson.orientation = config.xmInfo.orientation; 402 | manifestJson.versionName = config.xmInfo.versionName; 403 | manifestJson.versionCode = config.xmInfo.versionCode; 404 | manifestJson.minPlatformVersion = config.xmInfo.minPlatformVersion; 405 | manifestJson.icon = `/layaicon/${path.basename(config.xmInfo.icon)}`; 406 | fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8"); 407 | 408 | // 修改main.js文件 409 | let content = 'require("./qg-adapter.js");\nrequire("./libs/laya.xmmini.js");\nrequire("./index.js");'; 410 | let mainJsPath = path.join(projDir, "main.js"); 411 | fs.writeFileSync(mainJsPath, content, "utf8"); 412 | 413 | // 小米项目,修改index.js 414 | let filePath = path.join(projDir, "index.js"); 415 | if (!fs.existsSync(filePath)) { 416 | return; 417 | } 418 | let fileContent = fs.readFileSync(filePath, "utf8"); 419 | fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./"); 420 | fs.writeFileSync(filePath, fileContent, "utf8"); 421 | }) 422 | 423 | // 打包rpk 424 | gulp.task("buildRPK_XM", ["modifyFile_XM"], function() { 425 | // 如果不是小米快游戏 426 | if (platform !== "xmgame") { 427 | return; 428 | } 429 | // 在小米轻游戏项目目录中执行: 430 | // npm run build || npm run release 431 | let cmdStr = "build"; 432 | if (config.xmInfo.useReleaseSign) { 433 | cmdStr = "release"; 434 | } 435 | return new Promise((resolve, reject) => { 436 | let cmd = `npm${commandSuffix}`; 437 | let args = ["run", cmdStr]; 438 | let opts = { 439 | cwd: projDir 440 | }; 441 | let cp = childProcess.spawn(cmd, args, opts); 442 | // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']); 443 | cp.stdout.on('data', (data) => { 444 | console.log(`stdout: ${data}`); 445 | }); 446 | 447 | cp.stderr.on('data', (data) => { 448 | console.log(`stderr: ${data}`); 449 | // reject(); 450 | }); 451 | 452 | cp.on('close', (code) => { 453 | console.log(`子进程退出码:${code}`); 454 | resolve(); 455 | }); 456 | }); 457 | }); 458 | 459 | gulp.task("showQRCode_XM", ["buildRPK_XM"], function() { 460 | // 如果不是小米快游戏 461 | if (platform !== "xmgame") { 462 | return; 463 | } 464 | // 在小米轻游戏项目目录中执行: 465 | // npm run server 466 | return new Promise((resolve, reject) => { 467 | let cmd = `npm${commandSuffix}`; 468 | let args = ["run", "server"]; 469 | let opts = { 470 | cwd: projDir 471 | }; 472 | let cp = childProcess.spawn(cmd, args, opts); 473 | // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']); 474 | cp.stdout.on('data', (data) => { 475 | console.log(`${data}`); 476 | // 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程 477 | console.log('xm_qrcode_pid:' + cp.pid); 478 | }); 479 | 480 | cp.stderr.on('data', (data) => { 481 | console.log(`stderr: ${data}`); 482 | // reject(); 483 | }); 484 | 485 | cp.on('close', (code) => { 486 | console.log(`子进程退出码:${code}`); 487 | resolve(); 488 | }); 489 | }); 490 | }); 491 | 492 | 493 | gulp.task("buildXiaomiProj", ["showQRCode_XM"], function() { 494 | console.log("all tasks completed"); 495 | }); -------------------------------------------------------------------------------- /.laya/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "autoHeader": { 3 | "format": { 4 | "startWith": "/**", 5 | "middleWith": "*", 6 | "endWith": "*/", 7 | "headerPrefix": "@", 8 | }, 9 | "header": { 10 | "Description": " ", 11 | "Author": "lzh", 12 | "Date": { 13 | "type": "createTime", 14 | "format": "YYYY-MM-DD HH:mm:ss", 15 | }, 16 | "Last Modified by": { 17 | "type": "modifier", 18 | "value": "lzh", 19 | }, 20 | "Last Modified time": { 21 | "type": "modifyTime", 22 | "format": "YYYY-MM-DD HH:mm:ss" 23 | }, 24 | "copyright": "youai" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /bin/DP1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/bin/DP1.png -------------------------------------------------------------------------------- /bin/fileconfig.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /bin/game.js: -------------------------------------------------------------------------------- 1 | if ((typeof swan !== 'undefined') && (typeof swanGlobal !== 'undefined')) { 2 | require("swan-game-adapter.js"); 3 | require("libs/laya.bdmini.js"); 4 | } else if (typeof wx!=="undefined") { 5 | require("weapp-adapter.js"); 6 | require("libs/laya.wxmini.js"); 7 | } 8 | window.loadLib = require; 9 | require("index.js"); -------------------------------------------------------------------------------- /bin/game.json: -------------------------------------------------------------------------------- 1 | { 2 | "deviceOrientation": "portrait", 3 | "showStatusBar": false, 4 | "networkTimeout": { 5 | "request": 10000, 6 | "connectSocket": 10000, 7 | "uploadFile": 10000, 8 | "downloadFile": 10000 9 | } 10 | } -------------------------------------------------------------------------------- /bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | www.layabox.com 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置LayaNative屏幕方向,可设置以下值 3 | * landscape 横屏 4 | * portrait 竖屏 5 | * sensor_landscape 横屏(双方向) 6 | * sensor_portrait 竖屏(双方向) 7 | */ 8 | window.screenOrientation = "sensor_landscape"; 9 | 10 | //-----libs-begin----- 11 | loadLib("libs/laya.core.js") 12 | loadLib("libs/laya.ui.js") 13 | loadLib("libs/laya.physics.js") 14 | //-----libs-end------- 15 | loadLib("js/bundle.js"); 16 | -------------------------------------------------------------------------------- /bin/joystickView.json: -------------------------------------------------------------------------------- 1 | {"type":"Scene","props":{"width":250,"height":250},"compId":2,"child":[{"type":"Sprite","props":{"y":125,"x":125,"width":250,"var":"joystickBg","texture":"DP1.png","pivotY":125,"pivotX":125,"name":"joystickBg","height":250},"compId":3},{"type":"Sprite","props":{"y":124,"x":124,"width":100,"var":"joystickPoint","texture":"yangan.png","pivotY":50,"pivotX":50,"name":"joystickPoint","height":100},"compId":6}],"loadList":["DP1.png","yangan.png"],"loadList3D":[]} -------------------------------------------------------------------------------- /bin/js/bundle.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || (function () { 2 | var extendStatics = Object.setPrototypeOf || 3 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 4 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 5 | return function (d, b) { 6 | extendStatics(d, b); 7 | function __() { this.constructor = d; } 8 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 9 | }; 10 | })(); 11 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i this._MaxMoveDistance * this._MaxMoveDistance) { 109 | this.visible = true; 110 | this._isTouchMove = true; 111 | } 112 | } 113 | else { 114 | //将移动时的鼠标屏幕坐标转化为摇杆局部坐标 115 | var locationPos = this.globalToLocal(new Laya.Point(Laya.stage.mouseX, Laya.stage.mouseY), false); 116 | //更新摇杆控制点位置 117 | this.joystickPoint.pos(locationPos.x, locationPos.y); 118 | //更新控制点与摇杆中心点位置距离 119 | this._deltaX = locationPos.x - this._originPiont.x; 120 | this._deltaY = locationPos.y - this._originPiont.y; 121 | //计算控制点在摇杆中的角度 122 | var dx = this._deltaX * this._deltaX; 123 | var dy = this._deltaY * this._deltaY; 124 | this.angle = Math.atan2(this._deltaX, this._deltaY) * 180 / Math.PI; 125 | if (this.angle < 0) 126 | this.angle += 360; 127 | //对角度取整 128 | this.angle = Math.round(this.angle); 129 | //计算控制点在摇杆中的弧度 130 | this.radians = Math.PI / 180 * this.angle; 131 | //强制控制点与中心距离 132 | if (dx + dy >= this._joystickRadius * this._joystickRadius) { 133 | //控制点在半径的位置(根据弧度变化) 134 | var x = Math.floor(Math.sin(this.radians) * this._joystickRadius + this._originPiont.x); 135 | var y = Math.floor(Math.cos(this.radians) * this._joystickRadius + this._originPiont.y); 136 | this.joystickPoint.pos(x, y); 137 | } 138 | else { 139 | //不超过取原坐标 140 | this.joystickPoint.pos(locationPos.x, locationPos.y); 141 | } 142 | } 143 | }; 144 | /** 145 | * 鼠标抬起事件回调 146 | * @param evt 147 | */ 148 | JoyStick.prototype._onMouseUp = function (evt) { 149 | // 如果不是上次的点击id,返回(避免多点抬起,以第一次按下id为准) 150 | if (evt.touchId != this._curTouchId) 151 | return; 152 | this.visible = false; 153 | this._touchRect.off(Event.MOUSE_MOVE, this, this._onMouseMove); 154 | //修改摇杆角度与弧度为-1(代表无角度) 155 | this.radians = this.angle = -1; 156 | }; 157 | /** 158 | * 两点距离的平方 159 | * @param srcX 起始点X值 160 | * @param srcY 起始点Y值 161 | * @param desX 目标点X值 162 | * @param desY 目标点Y值 163 | */ 164 | JoyStick.prototype.distanceSquare = function (srcX, srcY, desX, desY) { 165 | return (desX - srcX) * (desX - srcX) + (desY - srcY) * (desY - srcY); 166 | }; 167 | return JoyStick; 168 | }(layaMaxUI_1.ui.joystickViewUI)); 169 | exports.JoyStick = JoyStick; 170 | },{"./ui/layaMaxUI":4}],3:[function(require,module,exports){ 171 | "use strict"; 172 | Object.defineProperty(exports, "__esModule", { value: true }); 173 | var GameConfig_1 = require("./GameConfig"); 174 | var JoyStick_1 = require("./JoyStick"); 175 | var Main = /** @class */ (function () { 176 | function Main() { 177 | //根据IDE设置初始化引擎 178 | if (window["Laya3D"]) 179 | Laya3D.init(GameConfig_1.default.width, GameConfig_1.default.height); 180 | else 181 | Laya.init(GameConfig_1.default.width, GameConfig_1.default.height, Laya["WebGL"]); 182 | Laya["Physics"] && Laya["Physics"].enable(); 183 | Laya["DebugPanel"] && Laya["DebugPanel"].enable(); 184 | Laya.stage.scaleMode = GameConfig_1.default.scaleMode; 185 | Laya.stage.screenMode = GameConfig_1.default.screenMode; 186 | //兼容微信不支持加载scene后缀场景 187 | Laya.URL.exportSceneToJson = GameConfig_1.default.exportSceneToJson; 188 | //打开调试面板(通过IDE设置调试模式,或者url地址增加debug=true参数,均可打开调试面板) 189 | if (GameConfig_1.default.debug || Laya.Utils.getQueryString("debug") == "true") 190 | Laya.enableDebugPanel(); 191 | if (GameConfig_1.default.physicsDebug && Laya["PhysicsDebugDraw"]) 192 | Laya["PhysicsDebugDraw"].enable(); 193 | if (GameConfig_1.default.stat) 194 | Laya.Stat.show(); 195 | Laya.alertGlobalError = true; 196 | //激活资源版本控制,version.json由IDE发布功能自动生成,如果没有也不影响后续流程 197 | Laya.ResourceVersion.enable("version.json", Laya.Handler.create(this, this.onVersionLoaded), Laya.ResourceVersion.FILENAME_VERSION); 198 | } 199 | Main.prototype.onVersionLoaded = function () { 200 | //激活大小图映射,加载小图的时候,如果发现小图在大图合集里面,则优先加载大图合集,而不是小图 201 | Laya.AtlasInfoManager.enable("fileconfig.json", Laya.Handler.create(this, this.onConfigLoaded)); 202 | }; 203 | Main.prototype.onConfigLoaded = function () { 204 | //加载IDE指定的场景 205 | GameConfig_1.default.startScene && Laya.Scene.open(GameConfig_1.default.startScene); 206 | Main.joystick = new JoyStick_1.JoyStick(Laya.stage); 207 | Laya.stage.addChild(Main.joystick); 208 | }; 209 | return Main; 210 | }()); 211 | //激活启动类 212 | new Main(); 213 | },{"./GameConfig":1,"./JoyStick":2}],4:[function(require,module,exports){ 214 | "use strict"; 215 | Object.defineProperty(exports, "__esModule", { value: true }); 216 | var Scene = Laya.Scene; 217 | var REG = Laya.ClassUtils.regClass; 218 | var ui; 219 | (function (ui) { 220 | var joystickViewUI = /** @class */ (function (_super) { 221 | __extends(joystickViewUI, _super); 222 | function joystickViewUI() { 223 | return _super.call(this) || this; 224 | } 225 | joystickViewUI.prototype.createChildren = function () { 226 | _super.prototype.createChildren.call(this); 227 | this.loadScene("joystickView"); 228 | }; 229 | return joystickViewUI; 230 | }(Scene)); 231 | ui.joystickViewUI = joystickViewUI; 232 | REG("ui.joystickViewUI", joystickViewUI); 233 | var mainViewUI = /** @class */ (function (_super) { 234 | __extends(mainViewUI, _super); 235 | function mainViewUI() { 236 | return _super.call(this) || this; 237 | } 238 | mainViewUI.prototype.createChildren = function () { 239 | _super.prototype.createChildren.call(this); 240 | this.loadScene("mainView"); 241 | }; 242 | return mainViewUI; 243 | }(Scene)); 244 | ui.mainViewUI = mainViewUI; 245 | REG("ui.mainViewUI", mainViewUI); 246 | })(ui = exports.ui || (exports.ui = {})); 247 | },{}]},{},[3]) 248 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkQ6L0xheWFBaXJJREUgKDIpL3Jlc291cmNlcy9hcHAvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsInNyYy9HYW1lQ29uZmlnLnRzIiwic3JjL0pveVN0aWNrLnRzIiwic3JjL01haW4udHMiLCJzcmMvdWkvbGF5YU1heFVJLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkEsZ0dBQWdHOztBQUVoRzs7RUFFRTtBQUNGO0lBYUk7SUFBYyxDQUFDO0lBQ1IsZUFBSSxHQUFYO1FBQ0ksSUFBSSxHQUFHLEdBQWEsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUM7SUFFakQsQ0FBQztJQWhCTSxnQkFBSyxHQUFRLEdBQUcsQ0FBQztJQUNqQixpQkFBTSxHQUFRLElBQUksQ0FBQztJQUNuQixvQkFBUyxHQUFRLFlBQVksQ0FBQztJQUM5QixxQkFBVSxHQUFRLE1BQU0sQ0FBQztJQUN6QixpQkFBTSxHQUFRLEtBQUssQ0FBQztJQUNwQixpQkFBTSxHQUFRLE1BQU0sQ0FBQztJQUNyQixxQkFBVSxHQUFLLGdCQUFnQixDQUFDO0lBQ2hDLG9CQUFTLEdBQVEsRUFBRSxDQUFDO0lBQ3BCLGdCQUFLLEdBQVMsS0FBSyxDQUFDO0lBQ3BCLGVBQUksR0FBUyxLQUFLLENBQUM7SUFDbkIsdUJBQVksR0FBUyxLQUFLLENBQUM7SUFDM0IsNEJBQWlCLEdBQVMsSUFBSSxDQUFDO0lBTTFDLGlCQUFDO0NBbEJELEFBa0JDLElBQUE7a0JBbEJvQixVQUFVO0FBbUIvQixVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7OztBQ3hCbEI7Ozs7Ozs7R0FPRzs7QUFFSCw0Q0FBb0M7QUFDcEMsSUFBTyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztBQUUxQjtJQUE4Qiw0QkFBaUI7SUErQjNDLGtCQUFZLE9BQW9CO1FBQWhDLFlBQ0ksaUJBQU8sU0FNVjtRQXJDRCx3QkFBd0I7UUFDUCxzQkFBZ0IsR0FBVyxFQUFFLENBQUM7UUFtQi9DLGtDQUFrQztRQUMxQixpQkFBVyxHQUFXLENBQUMsQ0FBQyxDQUFDO1FBQ2pDLGtDQUFrQztRQUMxQixrQkFBWSxHQUFZLEtBQUssQ0FBQztRQUN0QyxXQUFXO1FBQ0osV0FBSyxHQUFXLENBQUMsQ0FBQyxDQUFDO1FBQzFCLFdBQVc7UUFDSixhQUFPLEdBQVcsQ0FBQyxDQUFDLENBQUM7UUFLeEIsS0FBSSxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUM7UUFFMUIsS0FBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxLQUFJLEVBQUUsS0FBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQzlELEtBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsS0FBSSxFQUFFLEtBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUMxRCxLQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLEtBQUksRUFBRSxLQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7O0lBQy9ELENBQUM7SUFFTSwwQkFBTyxHQUFkO1FBQ0ksSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNwQyxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzNFLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7UUFDekUsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7T0FHRztJQUNLLCtCQUFZLEdBQXBCLFVBQXFCLEdBQVU7UUFDM0IsVUFBVTtRQUNWLElBQUksQ0FBQyxXQUFXLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQztRQUMvQixXQUFXO1FBQ1gsSUFBSSxDQUFDLFlBQVksR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDO1FBQy9CLElBQUksQ0FBQyxZQUFZLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQztRQUMvQixJQUFJLENBQUMsWUFBWSxHQUFHLEtBQUssQ0FBQztRQUMxQixhQUFhO1FBQ2IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUN6RixZQUFZO1FBQ1osSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDL0QsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFFRDs7O09BR0c7SUFDSywrQkFBWSxHQUFwQixVQUFxQixHQUFVO1FBQzNCLHVDQUF1QztRQUN2QyxJQUFJLEdBQUcsQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLFdBQVc7WUFBRSxPQUFPO1FBQzVDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ2Ysb0JBQW9CO1lBQ3BCLElBQUksT0FBTyxHQUFXLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxJQUFJLENBQUMsWUFBWSxFQUFFLEdBQUcsQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3hHLElBQUksT0FBTyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7Z0JBQ3pELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO2dCQUNwQixJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQzthQUM1QjtTQUNKO2FBQU07WUFDSCxzQkFBc0I7WUFDdEIsSUFBSSxXQUFXLEdBQWUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM5RyxXQUFXO1lBQ1gsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDckQsaUJBQWlCO1lBQ2pCLElBQUksQ0FBQyxPQUFPLEdBQUcsV0FBVyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztZQUNuRCxJQUFJLENBQUMsT0FBTyxHQUFHLFdBQVcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7WUFDbkQsY0FBYztZQUNkLElBQUksRUFBRSxHQUFXLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztZQUM3QyxJQUFJLEVBQUUsR0FBVyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7WUFDN0MsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3BFLElBQUksSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDO2dCQUFFLElBQUksQ0FBQyxLQUFLLElBQUksR0FBRyxDQUFDO1lBQ3RDLE9BQU87WUFDUCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3BDLGNBQWM7WUFDZCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7WUFDMUMsWUFBWTtZQUNaLElBQUksRUFBRSxHQUFHLEVBQUUsSUFBSSxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxlQUFlLEVBQUU7Z0JBQ3hELG1CQUFtQjtnQkFDbkIsSUFBSSxDQUFDLEdBQVcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hHLElBQUksQ0FBQyxHQUFXLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNoRyxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7YUFDaEM7aUJBQ0k7Z0JBQ0QsU0FBUztnQkFDVCxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUN4RDtTQUNKO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNLLDZCQUFVLEdBQWxCLFVBQW1CLEdBQVU7UUFDekIsb0NBQW9DO1FBQ3BDLElBQUksR0FBRyxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsV0FBVztZQUFFLE9BQU87UUFDNUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDckIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQy9ELHFCQUFxQjtRQUNyQixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLGlDQUFjLEdBQXJCLFVBQXNCLElBQVksRUFBRSxJQUFZLEVBQUUsSUFBWSxFQUFFLElBQVk7UUFDeEUsT0FBTyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBQ0wsZUFBQztBQUFELENBdElBLEFBc0lDLENBdEk2QixjQUFFLENBQUMsY0FBYyxHQXNJOUM7QUF0SVksNEJBQVE7Ozs7QUNackIsMkNBQXNDO0FBQ3RDLHVDQUFzQztBQUN0QztJQUVDO1FBQ0MsZ0JBQWdCO1FBQ2hCLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsb0JBQVUsQ0FBQyxLQUFLLEVBQUUsb0JBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQzs7WUFDbEUsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBVSxDQUFDLEtBQUssRUFBRSxvQkFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNuRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzVDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDbEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLEdBQUcsb0JBQVUsQ0FBQyxTQUFTLENBQUM7UUFDNUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsb0JBQVUsQ0FBQyxVQUFVLENBQUM7UUFDOUMsb0JBQW9CO1FBQ3BCLElBQUksQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEdBQUcsb0JBQVUsQ0FBQyxpQkFBaUIsQ0FBQztRQUUxRCxvREFBb0Q7UUFDcEQsSUFBSSxvQkFBVSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsSUFBSSxNQUFNO1lBQUUsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDOUYsSUFBSSxvQkFBVSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsa0JBQWtCLENBQUM7WUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUMzRixJQUFJLG9CQUFVLENBQUMsSUFBSTtZQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdEMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQztRQUU3QixnREFBZ0Q7UUFDaEQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsSUFBSSxDQUFDLGVBQWUsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3JJLENBQUM7SUFFRCw4QkFBZSxHQUFmO1FBQ0MsK0NBQStDO1FBQy9DLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDO0lBQ2pHLENBQUM7SUFFRCw2QkFBYyxHQUFkO1FBQ0MsWUFBWTtRQUNaLG9CQUFVLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLG9CQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLG1CQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBQ0YsV0FBQztBQUFELENBbENBLEFBa0NDLElBQUE7QUFDRCxPQUFPO0FBQ1AsSUFBSSxJQUFJLEVBQUUsQ0FBQzs7OztBQ25DWCxJQUFPLEtBQUssR0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO0FBQ3hCLElBQUksR0FBRyxHQUFhLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDO0FBQzdDLElBQWMsRUFBRSxDQW1CZjtBQW5CRCxXQUFjLEVBQUU7SUFDWjtRQUFvQyxrQ0FBSztRQUdyQzttQkFBZSxpQkFBTztRQUFBLENBQUM7UUFDdkIsdUNBQWMsR0FBZDtZQUNJLGlCQUFNLGNBQWMsV0FBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDbkMsQ0FBQztRQUNMLHFCQUFDO0lBQUQsQ0FSQSxBQVFDLENBUm1DLEtBQUssR0FReEM7SUFSWSxpQkFBYyxpQkFRMUIsQ0FBQTtJQUNELEdBQUcsQ0FBQyxtQkFBbUIsRUFBQyxjQUFjLENBQUMsQ0FBQztJQUN4QztRQUFnQyw4QkFBSztRQUNqQzttQkFBZSxpQkFBTztRQUFBLENBQUM7UUFDdkIsbUNBQWMsR0FBZDtZQUNJLGlCQUFNLGNBQWMsV0FBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDL0IsQ0FBQztRQUNMLGlCQUFDO0lBQUQsQ0FOQSxBQU1DLENBTitCLEtBQUssR0FNcEM7SUFOWSxhQUFVLGFBTXRCLENBQUE7SUFDRCxHQUFHLENBQUMsZUFBZSxFQUFDLFVBQVUsQ0FBQyxDQUFDO0FBQ3BDLENBQUMsRUFuQmEsRUFBRSxHQUFGLFVBQUUsS0FBRixVQUFFLFFBbUJmIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgX19leHRlbmRzID0gKHRoaXMgJiYgdGhpcy5fX2V4dGVuZHMpIHx8IChmdW5jdGlvbiAoKSB7XHJcbiAgICB2YXIgZXh0ZW5kU3RhdGljcyA9IE9iamVjdC5zZXRQcm90b3R5cGVPZiB8fFxyXG4gICAgICAgICh7IF9fcHJvdG9fXzogW10gfSBpbnN0YW5jZW9mIEFycmF5ICYmIGZ1bmN0aW9uIChkLCBiKSB7IGQuX19wcm90b19fID0gYjsgfSkgfHxcclxuICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChiLmhhc093blByb3BlcnR5KHApKSBkW3BdID0gYltwXTsgfTtcclxuICAgIHJldHVybiBmdW5jdGlvbiAoZCwgYikge1xyXG4gICAgICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICAgICAgZnVuY3Rpb24gX18oKSB7IHRoaXMuY29uc3RydWN0b3IgPSBkOyB9XHJcbiAgICAgICAgZC5wcm90b3R5cGUgPSBiID09PSBudWxsID8gT2JqZWN0LmNyZWF0ZShiKSA6IChfXy5wcm90b3R5cGUgPSBiLnByb3RvdHlwZSwgbmV3IF9fKCkpO1xyXG4gICAgfTtcclxufSkoKTtcclxuKGZ1bmN0aW9uKCl7ZnVuY3Rpb24gcihlLG4sdCl7ZnVuY3Rpb24gbyhpLGYpe2lmKCFuW2ldKXtpZighZVtpXSl7dmFyIGM9XCJmdW5jdGlvblwiPT10eXBlb2YgcmVxdWlyZSYmcmVxdWlyZTtpZighZiYmYylyZXR1cm4gYyhpLCEwKTtpZih1KXJldHVybiB1KGksITApO3ZhciBhPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIraStcIidcIik7dGhyb3cgYS5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGF9dmFyIHA9bltpXT17ZXhwb3J0czp7fX07ZVtpXVswXS5jYWxsKHAuZXhwb3J0cyxmdW5jdGlvbihyKXt2YXIgbj1lW2ldWzFdW3JdO3JldHVybiBvKG58fHIpfSxwLHAuZXhwb3J0cyxyLGUsbix0KX1yZXR1cm4gbltpXS5leHBvcnRzfWZvcih2YXIgdT1cImZ1bmN0aW9uXCI9PXR5cGVvZiByZXF1aXJlJiZyZXF1aXJlLGk9MDtpPHQubGVuZ3RoO2krKylvKHRbaV0pO3JldHVybiBvfXJldHVybiByfSkoKSIsIi8qKlRoaXMgY2xhc3MgaXMgYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgYnkgTGF5YUFpcklERSwgcGxlYXNlIGRvIG5vdCBtYWtlIGFueSBtb2RpZmljYXRpb25zLiAqL1xyXG5cclxuLypcclxuKiDmuLjmiI/liJ3lp4vljJbphY3nva47XHJcbiovXHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEdhbWVDb25maWd7XHJcbiAgICBzdGF0aWMgd2lkdGg6bnVtYmVyPTcyMDtcclxuICAgIHN0YXRpYyBoZWlnaHQ6bnVtYmVyPTEyODA7XHJcbiAgICBzdGF0aWMgc2NhbGVNb2RlOnN0cmluZz1cImZpeGVkd2lkdGhcIjtcclxuICAgIHN0YXRpYyBzY3JlZW5Nb2RlOnN0cmluZz1cIm5vbmVcIjtcclxuICAgIHN0YXRpYyBhbGlnblY6c3RyaW5nPVwidG9wXCI7XHJcbiAgICBzdGF0aWMgYWxpZ25IOnN0cmluZz1cImxlZnRcIjtcclxuICAgIHN0YXRpYyBzdGFydFNjZW5lOmFueT1cIm1haW5WaWV3LnNjZW5lXCI7XHJcbiAgICBzdGF0aWMgc2NlbmVSb290OnN0cmluZz1cIlwiO1xyXG4gICAgc3RhdGljIGRlYnVnOmJvb2xlYW49ZmFsc2U7XHJcbiAgICBzdGF0aWMgc3RhdDpib29sZWFuPWZhbHNlO1xyXG4gICAgc3RhdGljIHBoeXNpY3NEZWJ1Zzpib29sZWFuPWZhbHNlO1xyXG4gICAgc3RhdGljIGV4cG9ydFNjZW5lVG9Kc29uOmJvb2xlYW49dHJ1ZTtcclxuICAgIGNvbnN0cnVjdG9yKCl7fVxyXG4gICAgc3RhdGljIGluaXQoKXtcclxuICAgICAgICB2YXIgcmVnOiBGdW5jdGlvbiA9IExheWEuQ2xhc3NVdGlscy5yZWdDbGFzcztcclxuXHJcbiAgICB9XHJcbn1cclxuR2FtZUNvbmZpZy5pbml0KCk7IiwiLyoqXHJcbiAqIEAgRGVzY3JpcHRpb246ICDmkYfmnYbmjqfliLbnsbtcclxuICogQCBBdXRob3I6IGx6aFxyXG4gKiBAIERhdGU6IDIwMTktMDctMTcgMTY6MzE6NTVcclxuICogQCBMYXN0IE1vZGlmaWVkIGJ5OiBsemhcclxuICogQCBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMTktMDctMTggMTc6MzM6MjZcclxuICogQCBjb3B5cmlnaHQ6IHlvdWFpXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdWkgfSBmcm9tIFwiLi91aS9sYXlhTWF4VUlcIjtcclxuaW1wb3J0IEV2ZW50ID0gTGF5YS5FdmVudDtcclxuXHJcbmV4cG9ydCBjbGFzcyBKb3lTdGljayBleHRlbmRzIHVpLmpveXN0aWNrVmlld1VJIHtcclxuICAgIC8qKuacgOWkp+a7keWKqOi3neemu++8iOi2hei/h+i3neemu+WImeaYvuekuuaTjee6teadhu+8iSAqL1xyXG4gICAgcHJpdmF0ZSByZWFkb25seSBfTWF4TW92ZURpc3RhbmNlOiBudW1iZXIgPSAxMDtcclxuICAgIC8qKuinpuaRuOWMuuWfnyAqL1xyXG4gICAgcHJpdmF0ZSBfdG91Y2hSZWN0OiBMYXlhLlNwcml0ZTtcclxuICAgIC8qKuaOp+WItuWZqOS4reW/g+eCuVjlnZDmoIcgKi9cclxuICAgIHByaXZhdGUgX29yaWdpblBpb250WDogbnVtYmVyO1xyXG4gICAgLyoq5o6n5Yi25Zmo5Lit5b+D54K5WeWdkOaghyAqL1xyXG4gICAgcHJpdmF0ZSBfb3JpZ2luUGlvbnRZOiBudW1iZXI7XHJcbiAgICAvKirmjqfliLblmajkuK3lv4PngrkgKi9cclxuICAgIHByaXZhdGUgX29yaWdpblBpb250OiBMYXlhLlBvaW50O1xyXG4gICAgLyoq5pON57q15p2G5LiO5o6n5Yi25Lit5b+D54K555qE6Led56a777yI5pGH5p2G55qE5ruR5Yqo6IyD5Zu077yJICovXHJcbiAgICBwcml2YXRlIF9qb3lzdGlja1JhZGl1czogbnVtYmVyO1xyXG4gICAgLyoq5pGH5p2G5LiO5Lit5b+D54K555qEeOi9tOi3neemuyAqL1xyXG4gICAgcHJpdmF0ZSBfZGVsdGFYOiBudW1iZXI7XHJcbiAgICAvKirmkYfmnYbkuI7kuK3lv4PngrnnmoR56L206Led56a7ICovXHJcbiAgICBwcml2YXRlIF9kZWx0YVk6IG51bWJlcjtcclxuICAgIC8qKiDlvIDlp4vngrnlh7vml7bnmoToiJ7lj7BY5Z2Q5qCHICovXHJcbiAgICBwcml2YXRlIF9zdGFydFN0YWdlWDogbnVtYmVyO1xyXG4gICAgLyoqIOW8gOWni+eCueWHu+aXtueahOiInuWPsFnlnZDmoIcgKi9cclxuICAgIHByaXZhdGUgX3N0YXJ0U3RhZ2VZOiBudW1iZXI7XHJcbiAgICAvKirlvZPliY3lpJrngrnop6bmkbhpZCDpmLLmraLmkYfmnYbkuIrlh7rnjrDnrKzkuozkuKrmiYvmjIfml7blubLmibDnrKzkuIDkuKrmiYvmjIcqL1xyXG4gICAgcHJpdmF0ZSBfY3VyVG91Y2hJZDogbnVtYmVyID0gLTE7XHJcbiAgICAvKirmmK/lkKbop6blj5FUb3VjaE1vdmXkuovku7bvvIzop6blj5HliJnmi6bmiKpDbGlja+S6i+S7tiAqL1xyXG4gICAgcHJpdmF0ZSBfaXNUb3VjaE1vdmU6IEJvb2xlYW4gPSBmYWxzZTtcclxuICAgIC8qKuaRh+adhueahOinkuW6piAqL1xyXG4gICAgcHVibGljIGFuZ2xlOiBudW1iZXIgPSAtMTtcclxuICAgIC8qKuaRh+adhueahOW8p+W6piAqL1xyXG4gICAgcHVibGljIHJhZGlhbnM6IG51bWJlciA9IC0xO1xyXG5cclxuXHJcbiAgICBjb25zdHJ1Y3Rvcih0b3VjaFNwOiBMYXlhLlNwcml0ZSkge1xyXG4gICAgICAgIHN1cGVyKCk7XHJcbiAgICAgICAgdGhpcy5fdG91Y2hSZWN0ID0gdG91Y2hTcDtcclxuXHJcbiAgICAgICAgdGhpcy5fdG91Y2hSZWN0Lm9uKEV2ZW50Lk1PVVNFX0RPV04sIHRoaXMsIHRoaXMuX29uTW91c2VEb3duKTtcclxuICAgICAgICB0aGlzLl90b3VjaFJlY3Qub24oRXZlbnQuTU9VU0VfVVAsIHRoaXMsIHRoaXMuX29uTW91c2VVcCk7XHJcbiAgICAgICAgdGhpcy5fdG91Y2hSZWN0Lm9uKEV2ZW50Lk1PVVNFX09VVCwgdGhpcywgdGhpcy5fb25Nb3VzZVVwKTtcclxuICAgIH1cclxuXHJcbiAgICBwdWJsaWMgb25Bd2FrZSgpOiB2b2lkIHtcclxuICAgICAgICB0aGlzLl9vcmlnaW5QaW9udFggPSB0aGlzLndpZHRoIC8gMjtcclxuICAgICAgICB0aGlzLl9vcmlnaW5QaW9udFkgPSB0aGlzLmhlaWdodCAvIDI7XHJcbiAgICAgICAgdGhpcy5fb3JpZ2luUGlvbnQgPSBuZXcgTGF5YS5Qb2ludCh0aGlzLl9vcmlnaW5QaW9udFgsIHRoaXMuX29yaWdpblBpb250WSk7XHJcbiAgICAgICAgdGhpcy5fam95c3RpY2tSYWRpdXMgPSB0aGlzLl9vcmlnaW5QaW9udFggLSB0aGlzLmpveXN0aWNrUG9pbnQud2lkdGggLyAyO1xyXG4gICAgICAgIHRoaXMudmlzaWJsZSA9IGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICog6byg5qCH5oyJ5LiL5LqL5Lu25Zue6LCDXHJcbiAgICAgKiBAcGFyYW0gZXZ0IFxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIF9vbk1vdXNlRG93bihldnQ6IEV2ZW50KTogdm9pZCB7XHJcbiAgICAgICAgLy/orrDlvZXlvZPliY3mjInkuItpZFxyXG4gICAgICAgIHRoaXMuX2N1clRvdWNoSWQgPSBldnQudG91Y2hJZDtcclxuICAgICAgICAvLyDorrDlvZXngrnlh7vnmoTlnZDmoIfngrlcclxuICAgICAgICB0aGlzLl9zdGFydFN0YWdlWCA9IGV2dC5zdGFnZVg7XHJcbiAgICAgICAgdGhpcy5fc3RhcnRTdGFnZVkgPSBldnQuc3RhZ2VZO1xyXG4gICAgICAgIHRoaXMuX2lzVG91Y2hNb3ZlID0gZmFsc2U7XHJcbiAgICAgICAgLy/mm7TmlrDmkYfmnYbliLDlsY/luZXmjInkuIvkvY3nva5cclxuICAgICAgICB0aGlzLnBvcyhMYXlhLnN0YWdlLm1vdXNlWCAtIHRoaXMuX29yaWdpblBpb250WCwgTGF5YS5zdGFnZS5tb3VzZVkgLSB0aGlzLl9vcmlnaW5QaW9udFkpO1xyXG4gICAgICAgIC8v5Yid5aeL5YyW5pGH5p2G5o6n5Yi254K55L2N572uXHJcbiAgICAgICAgdGhpcy5qb3lzdGlja1BvaW50LnBvcyh0aGlzLl9vcmlnaW5QaW9udFgsIHRoaXMuX29yaWdpblBpb250WSk7XHJcbiAgICAgICAgdGhpcy5fdG91Y2hSZWN0Lm9uKEV2ZW50Lk1PVVNFX01PVkUsIHRoaXMsIHRoaXMuX29uTW91c2VNb3ZlKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIOm8oOagh+enu+WKqOS6i+S7tuWbnuiwg1xyXG4gICAgICogQHBhcmFtIGV2dCBcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfb25Nb3VzZU1vdmUoZXZ0OiBFdmVudCk6IHZvaWQge1xyXG4gICAgICAgIC8v6Kej5Yaz5Zyo6K6+5aSH5LiK5ouW5Yqo5Yiw5bGP5bmV5aSW6Z2i5peg5rOV6Kem5Y+RTU9VU0VfT1VU5ZKMTU9VU0VfVVDkuovku7ZcclxuICAgICAgICBpZiAoZXZ0LnRvdWNoSWQgIT0gdGhpcy5fY3VyVG91Y2hJZCkgcmV0dXJuO1xyXG4gICAgICAgIGlmICghdGhpcy52aXNpYmxlKSB7XHJcbiAgICAgICAgICAgIC8vIOW9k+a7keWKqOi2hei/h+iuvuWumueahOi3neemu+aXtuaJjeaYvuekuuaTjee6teadhlxyXG4gICAgICAgICAgICBsZXQgbW92ZURpczogbnVtYmVyID0gdGhpcy5kaXN0YW5jZVNxdWFyZSh0aGlzLl9zdGFydFN0YWdlWCwgdGhpcy5fc3RhcnRTdGFnZVksIGV2dC5zdGFnZVgsIGV2dC5zdGFnZVkpO1xyXG4gICAgICAgICAgICBpZiAobW92ZURpcyA+IHRoaXMuX01heE1vdmVEaXN0YW5jZSAqIHRoaXMuX01heE1vdmVEaXN0YW5jZSkge1xyXG4gICAgICAgICAgICAgICAgdGhpcy52aXNpYmxlID0gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2lzVG91Y2hNb3ZlID0gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIC8v5bCG56e75Yqo5pe255qE6byg5qCH5bGP5bmV5Z2Q5qCH6L2s5YyW5Li65pGH5p2G5bGA6YOo5Z2Q5qCHXHJcbiAgICAgICAgICAgIGxldCBsb2NhdGlvblBvczogTGF5YS5Qb2ludCA9IHRoaXMuZ2xvYmFsVG9Mb2NhbChuZXcgTGF5YS5Qb2ludChMYXlhLnN0YWdlLm1vdXNlWCwgTGF5YS5zdGFnZS5tb3VzZVkpLCBmYWxzZSk7XHJcbiAgICAgICAgICAgIC8v5pu05paw5pGH5p2G5o6n5Yi254K55L2N572uXHJcbiAgICAgICAgICAgIHRoaXMuam95c3RpY2tQb2ludC5wb3MobG9jYXRpb25Qb3MueCwgbG9jYXRpb25Qb3MueSk7XHJcbiAgICAgICAgICAgIC8v5pu05paw5o6n5Yi254K55LiO5pGH5p2G5Lit5b+D54K55L2N572u6Led56a7XHJcbiAgICAgICAgICAgIHRoaXMuX2RlbHRhWCA9IGxvY2F0aW9uUG9zLnggLSB0aGlzLl9vcmlnaW5QaW9udC54O1xyXG4gICAgICAgICAgICB0aGlzLl9kZWx0YVkgPSBsb2NhdGlvblBvcy55IC0gdGhpcy5fb3JpZ2luUGlvbnQueTtcclxuICAgICAgICAgICAgLy/orqHnrpfmjqfliLbngrnlnKjmkYfmnYbkuK3nmoTop5LluqZcclxuICAgICAgICAgICAgbGV0IGR4OiBudW1iZXIgPSB0aGlzLl9kZWx0YVggKiB0aGlzLl9kZWx0YVg7XHJcbiAgICAgICAgICAgIGxldCBkeTogbnVtYmVyID0gdGhpcy5fZGVsdGFZICogdGhpcy5fZGVsdGFZO1xyXG4gICAgICAgICAgICB0aGlzLmFuZ2xlID0gTWF0aC5hdGFuMih0aGlzLl9kZWx0YVgsIHRoaXMuX2RlbHRhWSkgKiAxODAgLyBNYXRoLlBJO1xyXG4gICAgICAgICAgICBpZiAodGhpcy5hbmdsZSA8IDApIHRoaXMuYW5nbGUgKz0gMzYwO1xyXG4gICAgICAgICAgICAvL+WvueinkuW6puWPluaVtFxyXG4gICAgICAgICAgICB0aGlzLmFuZ2xlID0gTWF0aC5yb3VuZCh0aGlzLmFuZ2xlKTtcclxuICAgICAgICAgICAgLy/orqHnrpfmjqfliLbngrnlnKjmkYfmnYbkuK3nmoTlvKfluqZcclxuICAgICAgICAgICAgdGhpcy5yYWRpYW5zID0gTWF0aC5QSSAvIDE4MCAqIHRoaXMuYW5nbGU7XHJcbiAgICAgICAgICAgIC8v5by65Yi25o6n5Yi254K55LiO5Lit5b+D6Led56a7XHJcbiAgICAgICAgICAgIGlmIChkeCArIGR5ID49IHRoaXMuX2pveXN0aWNrUmFkaXVzICogdGhpcy5fam95c3RpY2tSYWRpdXMpIHtcclxuICAgICAgICAgICAgICAgIC8v5o6n5Yi254K55Zyo5Y2K5b6E55qE5L2N572u77yI5qC55o2u5byn5bqm5Y+Y5YyW77yJXHJcbiAgICAgICAgICAgICAgICBsZXQgeDogbnVtYmVyID0gTWF0aC5mbG9vcihNYXRoLnNpbih0aGlzLnJhZGlhbnMpICogdGhpcy5fam95c3RpY2tSYWRpdXMgKyB0aGlzLl9vcmlnaW5QaW9udC54KTtcclxuICAgICAgICAgICAgICAgIGxldCB5OiBudW1iZXIgPSBNYXRoLmZsb29yKE1hdGguY29zKHRoaXMucmFkaWFucykgKiB0aGlzLl9qb3lzdGlja1JhZGl1cyArIHRoaXMuX29yaWdpblBpb250LnkpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5qb3lzdGlja1BvaW50LnBvcyh4LCB5KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgIC8v5LiN6LaF6L+H5Y+W5Y6f5Z2Q5qCHXHJcbiAgICAgICAgICAgICAgICB0aGlzLmpveXN0aWNrUG9pbnQucG9zKGxvY2F0aW9uUG9zLngsIGxvY2F0aW9uUG9zLnkpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICog6byg5qCH5oqs6LW35LqL5Lu25Zue6LCDXHJcbiAgICAgKiBAcGFyYW0gZXZ0IFxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIF9vbk1vdXNlVXAoZXZ0OiBFdmVudCk6IHZvaWQge1xyXG4gICAgICAgIC8vIOWmguaenOS4jeaYr+S4iuasoeeahOeCueWHu2lk77yM6L+U5Zue77yI6YG/5YWN5aSa54K55oqs6LW377yM5Lul56ys5LiA5qyh5oyJ5LiLaWTkuLrlh4bvvIlcclxuICAgICAgICBpZiAoZXZ0LnRvdWNoSWQgIT0gdGhpcy5fY3VyVG91Y2hJZCkgcmV0dXJuO1xyXG4gICAgICAgIHRoaXMudmlzaWJsZSA9IGZhbHNlO1xyXG4gICAgICAgIHRoaXMuX3RvdWNoUmVjdC5vZmYoRXZlbnQuTU9VU0VfTU9WRSwgdGhpcywgdGhpcy5fb25Nb3VzZU1vdmUpO1xyXG4gICAgICAgIC8v5L+u5pS55pGH5p2G6KeS5bqm5LiO5byn5bqm5Li6LTHvvIjku6Pooajml6Dop5LluqbvvIlcclxuICAgICAgICB0aGlzLnJhZGlhbnMgPSB0aGlzLmFuZ2xlID0gLTE7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiDkuKTngrnot53nprvnmoTlubPmlrlcclxuICAgICAqIEBwYXJhbSBzcmNYIOi1t+Wni+eCuVjlgLxcclxuICAgICAqIEBwYXJhbSBzcmNZIOi1t+Wni+eCuVnlgLxcclxuICAgICAqIEBwYXJhbSBkZXNYIOebruagh+eCuVjlgLxcclxuICAgICAqIEBwYXJhbSBkZXNZIOebruagh+eCuVnlgLxcclxuICAgICAqL1xyXG4gICAgcHVibGljIGRpc3RhbmNlU3F1YXJlKHNyY1g6IG51bWJlciwgc3JjWTogbnVtYmVyLCBkZXNYOiBudW1iZXIsIGRlc1k6IG51bWJlcik6IG51bWJlciB7XHJcbiAgICAgICAgcmV0dXJuIChkZXNYIC0gc3JjWCkgKiAoZGVzWCAtIHNyY1gpICsgKGRlc1kgLSBzcmNZKSAqIChkZXNZIC0gc3JjWSk7XHJcbiAgICB9XHJcbn0iLCJpbXBvcnQgR2FtZUNvbmZpZyBmcm9tIFwiLi9HYW1lQ29uZmlnXCI7XHJcbmltcG9ydCB7IEpveVN0aWNrIH0gZnJvbSBcIi4vSm95U3RpY2tcIjtcclxuY2xhc3MgTWFpbiB7XHJcblx0cHVibGljIHN0YXRpYyBqb3lzdGljazogSm95U3RpY2s7XHJcblx0Y29uc3RydWN0b3IoKSB7XHJcblx0XHQvL+agueaNrklEReiuvue9ruWIneWni+WMluW8leaTjlx0XHRcclxuXHRcdGlmICh3aW5kb3dbXCJMYXlhM0RcIl0pIExheWEzRC5pbml0KEdhbWVDb25maWcud2lkdGgsIEdhbWVDb25maWcuaGVpZ2h0KTtcclxuXHRcdGVsc2UgTGF5YS5pbml0KEdhbWVDb25maWcud2lkdGgsIEdhbWVDb25maWcuaGVpZ2h0LCBMYXlhW1wiV2ViR0xcIl0pO1xyXG5cdFx0TGF5YVtcIlBoeXNpY3NcIl0gJiYgTGF5YVtcIlBoeXNpY3NcIl0uZW5hYmxlKCk7XHJcblx0XHRMYXlhW1wiRGVidWdQYW5lbFwiXSAmJiBMYXlhW1wiRGVidWdQYW5lbFwiXS5lbmFibGUoKTtcclxuXHRcdExheWEuc3RhZ2Uuc2NhbGVNb2RlID0gR2FtZUNvbmZpZy5zY2FsZU1vZGU7XHJcblx0XHRMYXlhLnN0YWdlLnNjcmVlbk1vZGUgPSBHYW1lQ29uZmlnLnNjcmVlbk1vZGU7XHJcblx0XHQvL+WFvOWuueW+ruS/oeS4jeaUr+aMgeWKoOi9vXNjZW5l5ZCO57yA5Zy65pmvXHJcblx0XHRMYXlhLlVSTC5leHBvcnRTY2VuZVRvSnNvbiA9IEdhbWVDb25maWcuZXhwb3J0U2NlbmVUb0pzb247XHJcblxyXG5cdFx0Ly/miZPlvIDosIPor5XpnaLmnb/vvIjpgJrov4dJREXorr7nva7osIPor5XmqKHlvI/vvIzmiJbogIV1cmzlnLDlnYDlop7liqBkZWJ1Zz10cnVl5Y+C5pWw77yM5Z2H5Y+v5omT5byA6LCD6K+V6Z2i5p2/77yJXHJcblx0XHRpZiAoR2FtZUNvbmZpZy5kZWJ1ZyB8fCBMYXlhLlV0aWxzLmdldFF1ZXJ5U3RyaW5nKFwiZGVidWdcIikgPT0gXCJ0cnVlXCIpIExheWEuZW5hYmxlRGVidWdQYW5lbCgpO1xyXG5cdFx0aWYgKEdhbWVDb25maWcucGh5c2ljc0RlYnVnICYmIExheWFbXCJQaHlzaWNzRGVidWdEcmF3XCJdKSBMYXlhW1wiUGh5c2ljc0RlYnVnRHJhd1wiXS5lbmFibGUoKTtcclxuXHRcdGlmIChHYW1lQ29uZmlnLnN0YXQpIExheWEuU3RhdC5zaG93KCk7XHJcblx0XHRMYXlhLmFsZXJ0R2xvYmFsRXJyb3IgPSB0cnVlO1xyXG5cclxuXHRcdC8v5r+A5rS76LWE5rqQ54mI5pys5o6n5Yi277yMdmVyc2lvbi5qc29u55SxSURF5Y+R5biD5Yqf6IO96Ieq5Yqo55Sf5oiQ77yM5aaC5p6c5rKh5pyJ5Lmf5LiN5b2x5ZON5ZCO57ut5rWB56iLXHJcblx0XHRMYXlhLlJlc291cmNlVmVyc2lvbi5lbmFibGUoXCJ2ZXJzaW9uLmpzb25cIiwgTGF5YS5IYW5kbGVyLmNyZWF0ZSh0aGlzLCB0aGlzLm9uVmVyc2lvbkxvYWRlZCksIExheWEuUmVzb3VyY2VWZXJzaW9uLkZJTEVOQU1FX1ZFUlNJT04pO1xyXG5cdH1cclxuXHJcblx0b25WZXJzaW9uTG9hZGVkKCk6IHZvaWQge1xyXG5cdFx0Ly/mv4DmtLvlpKflsI/lm77mmKDlsITvvIzliqDovb3lsI/lm77nmoTml7blgJnvvIzlpoLmnpzlj5HnjrDlsI/lm77lnKjlpKflm77lkIjpm4bph4zpnaLvvIzliJnkvJjlhYjliqDovb3lpKflm77lkIjpm4bvvIzogIzkuI3mmK/lsI/lm75cclxuXHRcdExheWEuQXRsYXNJbmZvTWFuYWdlci5lbmFibGUoXCJmaWxlY29uZmlnLmpzb25cIiwgTGF5YS5IYW5kbGVyLmNyZWF0ZSh0aGlzLCB0aGlzLm9uQ29uZmlnTG9hZGVkKSk7XHJcblx0fVxyXG5cclxuXHRvbkNvbmZpZ0xvYWRlZCgpOiB2b2lkIHtcclxuXHRcdC8v5Yqg6L29SURF5oyH5a6a55qE5Zy65pmvXHJcblx0XHRHYW1lQ29uZmlnLnN0YXJ0U2NlbmUgJiYgTGF5YS5TY2VuZS5vcGVuKEdhbWVDb25maWcuc3RhcnRTY2VuZSk7XHJcblx0XHRNYWluLmpveXN0aWNrID0gbmV3IEpveVN0aWNrKExheWEuc3RhZ2UpO1xyXG5cdFx0TGF5YS5zdGFnZS5hZGRDaGlsZChNYWluLmpveXN0aWNrKTtcclxuXHR9XHJcbn1cclxuLy/mv4DmtLvlkK/liqjnsbtcclxubmV3IE1haW4oKTtcclxuIiwiLyoqVGhpcyBjbGFzcyBpcyBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBieSBMYXlhQWlySURFLCBwbGVhc2UgZG8gbm90IG1ha2UgYW55IG1vZGlmaWNhdGlvbnMuICovXG5pbXBvcnQgVmlldz1MYXlhLlZpZXc7XHJcbmltcG9ydCBEaWFsb2c9TGF5YS5EaWFsb2c7XHJcbmltcG9ydCBTY2VuZT1MYXlhLlNjZW5lO1xudmFyIFJFRzogRnVuY3Rpb24gPSBMYXlhLkNsYXNzVXRpbHMucmVnQ2xhc3M7XG5leHBvcnQgbW9kdWxlIHVpIHtcclxuICAgIGV4cG9ydCBjbGFzcyBqb3lzdGlja1ZpZXdVSSBleHRlbmRzIFNjZW5lIHtcclxuXHRcdHB1YmxpYyBqb3lzdGlja0JnOkxheWEuSW1hZ2U7XG5cdFx0cHVibGljIGpveXN0aWNrUG9pbnQ6TGF5YS5JbWFnZTtcbiAgICAgICAgY29uc3RydWN0b3IoKXsgc3VwZXIoKX1cclxuICAgICAgICBjcmVhdGVDaGlsZHJlbigpOnZvaWQge1xyXG4gICAgICAgICAgICBzdXBlci5jcmVhdGVDaGlsZHJlbigpO1xyXG4gICAgICAgICAgICB0aGlzLmxvYWRTY2VuZShcImpveXN0aWNrVmlld1wiKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBSRUcoXCJ1aS5qb3lzdGlja1ZpZXdVSVwiLGpveXN0aWNrVmlld1VJKTtcclxuICAgIGV4cG9ydCBjbGFzcyBtYWluVmlld1VJIGV4dGVuZHMgU2NlbmUge1xyXG4gICAgICAgIGNvbnN0cnVjdG9yKCl7IHN1cGVyKCl9XHJcbiAgICAgICAgY3JlYXRlQ2hpbGRyZW4oKTp2b2lkIHtcclxuICAgICAgICAgICAgc3VwZXIuY3JlYXRlQ2hpbGRyZW4oKTtcclxuICAgICAgICAgICAgdGhpcy5sb2FkU2NlbmUoXCJtYWluVmlld1wiKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBSRUcoXCJ1aS5tYWluVmlld1VJXCIsbWFpblZpZXdVSSk7XHJcbn1cciJdfQ== 249 | -------------------------------------------------------------------------------- /bin/libs/laya.d3Plugin.js: -------------------------------------------------------------------------------- 1 | 2 | (function(window,document,Laya){ 3 | var __un=Laya.un,__uns=Laya.uns,__static=Laya.static,__class=Laya.class,__getset=Laya.getset,__newvec=Laya.__newvec; 4 | 5 | var Component=laya.components.Component,MeshTerrainSprite3D=laya.d3.core.MeshTerrainSprite3D; 6 | /** 7 | *PathFinding 类用于创建寻路。 8 | */ 9 | //class laya.d3.component.PathFind extends laya.components.Component 10 | var PathFind=(function(_super){ 11 | function PathFind(){ 12 | /**@private */ 13 | this._meshTerrainSprite3D=null; 14 | /**@private */ 15 | this._finder=null; 16 | /**@private */ 17 | this._setting=null; 18 | /**寻路网格。*/ 19 | this.grid=null; 20 | PathFind.__super.call(this); 21 | } 22 | 23 | __class(PathFind,'laya.d3.component.PathFind',_super); 24 | var __proto=PathFind.prototype; 25 | /** 26 | *@private 27 | *初始化载入蒙皮动画组件。 28 | *@param owner 所属精灵对象。 29 | */ 30 | __proto._onAdded=function(){ 31 | if (! (this.owner instanceof laya.d3.core.MeshTerrainSprite3D )) 32 | throw new Error("PathFinding: The owner must MeshTerrainSprite3D!"); 33 | this._meshTerrainSprite3D=this.owner; 34 | } 35 | 36 | /** 37 | *寻找路径。 38 | *@param startX 开始X。 39 | *@param startZ 开始Z。 40 | *@param endX 结束X。 41 | *@param endZ 结束Z。 42 | *@return 路径。 43 | */ 44 | __proto.findPath=function(startX,startZ,endX,endZ){ 45 | var minX=this._meshTerrainSprite3D.minX; 46 | var minZ=this._meshTerrainSprite3D.minZ; 47 | var cellX=this._meshTerrainSprite3D.width / this.grid.width; 48 | var cellZ=this._meshTerrainSprite3D.depth / this.grid.height; 49 | var halfCellX=cellX / 2; 50 | var halfCellZ=cellZ / 2; 51 | var gridStartX=Math.floor((startX-minX)/ cellX); 52 | var gridStartZ=Math.floor((startZ-minZ)/ cellZ); 53 | var gridEndX=Math.floor((endX-minX)/ cellX); 54 | var gridEndZ=Math.floor((endZ-minZ)/ cellZ); 55 | var boundWidth=this.grid.width-1; 56 | var boundHeight=this.grid.height-1; 57 | (gridStartX > boundWidth)&& (gridStartX=boundWidth); 58 | (gridStartZ > boundHeight)&& (gridStartZ=boundHeight); 59 | (gridStartX < 0)&& (gridStartX=0); 60 | (gridStartZ < 0)&& (gridStartZ=0); 61 | (gridEndX > boundWidth)&& (gridEndX=boundWidth); 62 | (gridEndZ > boundHeight)&& (gridEndZ=boundHeight); 63 | (gridEndX < 0)&& (gridEndX=0); 64 | (gridEndZ < 0)&& (gridEndZ=0); 65 | var path=this._finder.findPath(gridStartX,gridStartZ,gridEndX,gridEndZ,this.grid); 66 | this.grid.reset(); 67 | for (var i=1;i < path.length-1;i++){ 68 | var gridPos=path[i]; 69 | gridPos[0]=gridPos[0] *cellX+halfCellX+minX; 70 | gridPos[1]=gridPos[1] *cellZ+halfCellZ+minZ; 71 | } 72 | if (path.length==1){ 73 | path[0][0]=endX; 74 | path[0][1]=endX; 75 | }else if (path.length > 1){ 76 | path[0][0]=startX; 77 | path[0][1]=startZ; 78 | path[path.length-1][0]=endX; 79 | path[path.length-1][1]=endZ; 80 | } 81 | return path; 82 | } 83 | 84 | /** 85 | *设置寻路设置。 86 | *@param value 寻路设置。 87 | */ 88 | /** 89 | *获取寻路设置。 90 | *@return 寻路设置。 91 | */ 92 | __getset(0,__proto,'setting',function(){ 93 | return this._setting; 94 | },function(value){ 95 | (value)&& (this._finder=new PathFinding.finders.AStarFinder(value)); 96 | this._setting=value; 97 | }); 98 | 99 | return PathFind; 100 | })(Component) 101 | 102 | 103 | 104 | })(window,document,Laya); 105 | -------------------------------------------------------------------------------- /bin/libs/laya.device.js: -------------------------------------------------------------------------------- 1 | 2 | (function(window,document,Laya){ 3 | var __un=Laya.un,__uns=Laya.uns,__static=Laya.static,__class=Laya.class,__getset=Laya.getset,__newvec=Laya.__newvec; 4 | 5 | var Bitmap=laya.resource.Bitmap,Browser=laya.utils.Browser,Event=laya.events.Event,EventDispatcher=laya.events.EventDispatcher; 6 | var Handler=laya.utils.Handler,LayaGL=laya.layagl.LayaGL,Rectangle=laya.maths.Rectangle,Render=laya.renders.Render; 7 | var Sprite=laya.display.Sprite,Stage=laya.display.Stage,Texture=laya.resource.Texture,Utils=laya.utils.Utils; 8 | var WebGL=laya.webgl.WebGL,WebGLContext=laya.webgl.WebGLContext; 9 | /** 10 | *使用前可用supported查看浏览器支持。 11 | */ 12 | //class laya.device.geolocation.Geolocation 13 | var Geolocation=(function(){ 14 | function Geolocation(){} 15 | __class(Geolocation,'laya.device.geolocation.Geolocation'); 16 | Geolocation.getCurrentPosition=function(onSuccess,onError){ 17 | Geolocation.navigator.geolocation.getCurrentPosition(function(pos){ 18 | Geolocation.position.setPosition(pos); 19 | onSuccess.runWith(Geolocation.position); 20 | }, 21 | function(error){ 22 | onError.runWith(error); 23 | },{ 24 | enableHighAccuracy :laya.device.geolocation.Geolocation.enableHighAccuracy, 25 | timeout :laya.device.geolocation.Geolocation.timeout, 26 | maximumAge :laya.device.geolocation.Geolocation.maximumAge 27 | }); 28 | } 29 | 30 | Geolocation.watchPosition=function(onSuccess,onError){ 31 | return Geolocation.navigator.geolocation.watchPosition(function(pos){ 32 | Geolocation.position.setPosition(pos); 33 | onSuccess.runWith(Geolocation.position); 34 | }, 35 | function(error){ 36 | onError.runWith(error); 37 | },{ 38 | enableHighAccuracy :Geolocation.enableHighAccuracy, 39 | timeout :Geolocation.timeout, 40 | maximumAge :Geolocation.maximumAge 41 | }); 42 | } 43 | 44 | Geolocation.clearWatch=function(id){ 45 | Geolocation.navigator.geolocation.clearWatch(id); 46 | } 47 | 48 | Geolocation.PERMISSION_DENIED=1; 49 | Geolocation.POSITION_UNAVAILABLE=2; 50 | Geolocation.TIMEOUT=3; 51 | Geolocation.enableHighAccuracy=false; 52 | Geolocation.maximumAge=0; 53 | __static(Geolocation, 54 | ['navigator',function(){return this.navigator=Browser.window.navigator;},'position',function(){return this.position=new GeolocationInfo();},'supported',function(){return this.supported=!!Geolocation.navigator.geolocation;},'timeout',function(){return this.timeout=1E10;} 55 | ]); 56 | return Geolocation; 57 | })() 58 | 59 | 60 | /** 61 | *Media用于捕捉摄像头和麦克风。可以捕捉任意之一,或者同时捕捉两者。getCamera前可以使用supported()检查当前浏览器是否支持。 62 | *NOTE: 63 | *

目前Media在移动平台只支持Android,不支持IOS。只可在FireFox完整地使用,Chrome测试时无法捕捉视频。

64 | */ 65 | //class laya.device.media.Media 66 | var Media=(function(){ 67 | function Media(){} 68 | __class(Media,'laya.device.media.Media'); 69 | Media.supported=function(){ 70 | return !!Browser.window.navigator.getUserMedia; 71 | } 72 | 73 | Media.getMedia=function(options,onSuccess,onError){ 74 | if (Browser.window.navigator.getUserMedia){ 75 | Browser.window.navigator.getUserMedia(options,function(stream){ 76 | onSuccess.runWith(Browser.window.URL.createObjectURL(stream)); 77 | },function(err){ 78 | onError.runWith(err); 79 | }); 80 | } 81 | } 82 | 83 | Media.__init$=function(){ 84 | /*__JS__ */navigator.getUserMedia=navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;; 85 | } 86 | 87 | return Media; 88 | })() 89 | 90 | 91 | /** 92 | *加速度x/y/z的单位均为m/s²。 93 | *在硬件(陀螺仪)不支持的情况下,alpha、beta和gamma值为null。 94 | * 95 | *@author Survivor 96 | */ 97 | //class laya.device.motion.AccelerationInfo 98 | var AccelerationInfo=(function(){ 99 | function AccelerationInfo(){ 100 | /** 101 | *x轴上的加速度值。 102 | */ 103 | this.x=NaN; 104 | /** 105 | *y轴上的加速度值。 106 | */ 107 | this.y=NaN; 108 | /** 109 | *z轴上的加速度值。 110 | */ 111 | this.z=NaN; 112 | } 113 | 114 | __class(AccelerationInfo,'laya.device.motion.AccelerationInfo'); 115 | return AccelerationInfo; 116 | })() 117 | 118 | 119 | //class laya.device.geolocation.GeolocationInfo 120 | var GeolocationInfo=(function(){ 121 | function GeolocationInfo(){ 122 | this.pos=null; 123 | this.coords=null; 124 | } 125 | 126 | __class(GeolocationInfo,'laya.device.geolocation.GeolocationInfo'); 127 | var __proto=GeolocationInfo.prototype; 128 | __proto.setPosition=function(pos){ 129 | this.pos=pos; 130 | this.coords=pos.coords; 131 | } 132 | 133 | __getset(0,__proto,'heading',function(){ 134 | return this.coords.heading; 135 | }); 136 | 137 | __getset(0,__proto,'latitude',function(){ 138 | return this.coords.latitude; 139 | }); 140 | 141 | __getset(0,__proto,'altitudeAccuracy',function(){ 142 | return this.coords.altitudeAccuracy; 143 | }); 144 | 145 | __getset(0,__proto,'longitude',function(){ 146 | return this.coords.longitude; 147 | }); 148 | 149 | __getset(0,__proto,'altitude',function(){ 150 | return this.coords.altitude; 151 | }); 152 | 153 | __getset(0,__proto,'accuracy',function(){ 154 | return this.coords.accuracy; 155 | }); 156 | 157 | __getset(0,__proto,'speed',function(){ 158 | return this.coords.speed; 159 | }); 160 | 161 | __getset(0,__proto,'timestamp',function(){ 162 | return this.pos.timestamp; 163 | }); 164 | 165 | return GeolocationInfo; 166 | })() 167 | 168 | 169 | /** 170 | *保存旋转信息的类。请勿修改本类的属性。 171 | *@author Survivor 172 | */ 173 | //class laya.device.motion.RotationInfo 174 | var RotationInfo=(function(){ 175 | function RotationInfo(){ 176 | /** 177 | *

178 | *指示设备是否可以提供绝对方位数据(指向地球坐标系),或者设备决定的任意坐标系。 179 | *关于坐标系参见https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained。 180 | *

181 | *需要注意的是,IOS环境下,该值始终为false。即使如此,你依旧可以从alpha中取得正确的值。 182 | */ 183 | this.absolute=false; 184 | /** 185 | *Z轴旋转角度,其值范围从0至360。 186 | *若absolute为true或者在IOS中,alpha值是从北方到当前设备方向的角度值。 187 | */ 188 | this.alpha=NaN; 189 | /** 190 | *X轴旋转角度,其值范围从-180至180。代表设备从前至后的运动。 191 | */ 192 | this.beta=NaN; 193 | /** 194 | *Y轴旋转角度,其值范围从-90至90。代表设备从左至右的运动。 195 | */ 196 | this.gamma=NaN; 197 | /** 198 | *罗盘数据的精确度(角度)。仅IOS可用。 199 | */ 200 | this.compassAccuracy=NaN; 201 | } 202 | 203 | __class(RotationInfo,'laya.device.motion.RotationInfo'); 204 | return RotationInfo; 205 | })() 206 | 207 | 208 | /** 209 | *使用Gyroscope.instance获取唯一的Gyroscope引用,请勿调用构造函数。 210 | * 211 | *

212 | *listen()的回调处理器接受两个参数: 213 | *function onOrientationChange(absolute:Boolean,info:RotationInfo):void 214 | *

    215 | *
  1. absolute:指示设备是否可以提供绝对方位数据(指向地球坐标系),或者设备决定的任意坐标系。关于坐标系参见https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained
  2. 216 | *
  3. info:RotationInfo类型参数,保存设备的旋转值。
  4. 217 | *
218 | *

219 | * 220 | *

221 | *浏览器兼容性参见:http://caniuse.com/#search=deviceorientation 222 | *

223 | */ 224 | //class laya.device.motion.Gyroscope extends laya.events.EventDispatcher 225 | var Gyroscope=(function(_super){ 226 | function Gyroscope(singleton){ 227 | Gyroscope.__super.call(this); 228 | /*__JS__ */this.onDeviceOrientationChange=this.onDeviceOrientationChange.bind(this); 229 | } 230 | 231 | __class(Gyroscope,'laya.device.motion.Gyroscope',_super); 232 | var __proto=Gyroscope.prototype; 233 | /** 234 | *监视陀螺仪运动。 235 | *@param observer 回调函数接受一个Boolean类型的absoluteGyroscopeInfo类型参数。 236 | */ 237 | __proto.on=function(type,caller,listener,args){ 238 | _super.prototype.on.call(this,type,caller,listener,args); 239 | Browser.window.addEventListener('deviceorientation',this.onDeviceOrientationChange); 240 | return this; 241 | } 242 | 243 | /** 244 | *取消指定处理器对陀螺仪的监视。 245 | *@param observer 246 | */ 247 | __proto.off=function(type,caller,listener,onceOnly){ 248 | (onceOnly===void 0)&& (onceOnly=false); 249 | if (!this.hasListener(type)) 250 | Browser.window.removeEventListener('deviceorientation',this.onDeviceOrientationChange); 251 | return _super.prototype.off.call(this,type,caller,listener,onceOnly); 252 | } 253 | 254 | __proto.onDeviceOrientationChange=function(e){ 255 | Gyroscope.info.alpha=e.alpha; 256 | Gyroscope.info.beta=e.beta; 257 | Gyroscope.info.gamma=e.gamma; 258 | if (e.webkitCompassHeading){ 259 | Gyroscope.info.alpha=e.webkitCompassHeading *-1; 260 | Gyroscope.info.compassAccuracy=e.webkitCompassAccuracy; 261 | } 262 | this.event(/*laya.events.Event.CHANGE*/"change",[e.absolute,Gyroscope.info]); 263 | } 264 | 265 | __getset(1,Gyroscope,'instance',function(){Gyroscope._instance=Gyroscope._instance|| new Gyroscope(0); 266 | return Gyroscope._instance; 267 | },laya.events.EventDispatcher._$SET_instance); 268 | 269 | Gyroscope._instance=null; 270 | __static(Gyroscope, 271 | ['info',function(){return this.info=new RotationInfo();} 272 | ]); 273 | return Gyroscope; 274 | })(EventDispatcher) 275 | 276 | 277 | /** 278 | *Shake只能在支持此操作的设备上有效。 279 | * 280 | *@author Survivor 281 | */ 282 | //class laya.device.Shake extends laya.events.EventDispatcher 283 | var Shake=(function(_super){ 284 | function Shake(){ 285 | this.throushold=0; 286 | this.shakeInterval=0; 287 | this.callback=null; 288 | this.lastX=NaN; 289 | this.lastY=NaN; 290 | this.lastZ=NaN; 291 | this.lastMillSecond=NaN; 292 | Shake.__super.call(this); 293 | } 294 | 295 | __class(Shake,'laya.device.Shake',_super); 296 | var __proto=Shake.prototype; 297 | /** 298 | *开始响应设备摇晃。 299 | *@param throushold 响应的瞬时速度阈值,轻度摇晃的值约在5~10间。 300 | *@param timeout 设备摇晃的响应间隔时间。 301 | *@param callback 在设备摇晃触发时调用的处理器。 302 | */ 303 | __proto.start=function(throushold,interval){ 304 | this.throushold=throushold; 305 | this.shakeInterval=interval; 306 | this.lastX=this.lastY=this.lastZ=NaN; 307 | Accelerator.instance.on(/*laya.events.Event.CHANGE*/"change",this,this.onShake); 308 | } 309 | 310 | /** 311 | *停止响应设备摇晃。 312 | */ 313 | __proto.stop=function(){ 314 | Accelerator.instance.off(/*laya.events.Event.CHANGE*/"change",this,this.onShake); 315 | } 316 | 317 | __proto.onShake=function(acceleration,accelerationIncludingGravity,rotationRate,interval){ 318 | if(isNaN(this.lastX)){ 319 | this.lastX=accelerationIncludingGravity.x; 320 | this.lastY=accelerationIncludingGravity.y; 321 | this.lastZ=accelerationIncludingGravity.z; 322 | this.lastMillSecond=Browser.now(); 323 | return; 324 | }; 325 | var deltaX=Math.abs(this.lastX-accelerationIncludingGravity.x); 326 | var deltaY=Math.abs(this.lastY-accelerationIncludingGravity.y); 327 | var deltaZ=Math.abs(this.lastZ-accelerationIncludingGravity.z); 328 | if(this.isShaked(deltaX,deltaY,deltaZ)){ 329 | var deltaMillSecond=Browser.now()-this.lastMillSecond; 330 | if (deltaMillSecond > this.shakeInterval){ 331 | this.event(/*laya.events.Event.CHANGE*/"change"); 332 | this.lastMillSecond=Browser.now(); 333 | } 334 | } 335 | this.lastX=accelerationIncludingGravity.x; 336 | this.lastY=accelerationIncludingGravity.y; 337 | this.lastZ=accelerationIncludingGravity.z; 338 | } 339 | 340 | // 通过任意两个分量判断是否满足摇晃设定。 341 | __proto.isShaked=function(deltaX,deltaY,deltaZ){ 342 | return (deltaX > this.throushold && deltaY > this.throushold)|| 343 | (deltaX > this.throushold && deltaZ > this.throushold)|| 344 | (deltaY > this.throushold && deltaZ > this.throushold) 345 | } 346 | 347 | __getset(1,Shake,'instance',function(){Shake._instance=Shake._instance|| new Shake(); 348 | return Shake._instance; 349 | },laya.events.EventDispatcher._$SET_instance); 350 | 351 | Shake._instance=null; 352 | return Shake; 353 | })(EventDispatcher) 354 | 355 | 356 | /** 357 | *Accelerator.instance获取唯一的Accelerator引用,请勿调用构造函数。 358 | * 359 | *

360 | *listen()的回调处理器接受四个参数: 361 | *

    362 | *
  1. acceleration:表示用户给予设备的加速度。
  2. 363 | *
  3. accelerationIncludingGravity:设备受到的总加速度(包含重力)。
  4. 364 | *
  5. rotationRate:设备的自转速率。
  6. 365 | *
  7. interval:加速度获取的时间间隔(毫秒)。
  8. 366 | *
367 | *

368 | *

369 | *NOTE
370 | *如,rotationRate的alpha在apple和moz文档中都是z轴旋转角度,但是实测是x轴旋转角度。为了使各属性表示的值与文档所述相同,实际值与其他属性进行了对调。 371 | *其中: 372 | *

377 | *目前孰是孰非尚未可知,以此为注。 378 | *

379 | */ 380 | //class laya.device.motion.Accelerator extends laya.events.EventDispatcher 381 | var Accelerator=(function(_super){ 382 | function Accelerator(singleton){ 383 | Accelerator.__super.call(this); 384 | /*__JS__ */this.onDeviceOrientationChange=this.onDeviceOrientationChange.bind(this); 385 | } 386 | 387 | __class(Accelerator,'laya.device.motion.Accelerator',_super); 388 | var __proto=Accelerator.prototype; 389 | /** 390 | *侦听加速器运动。 391 | *@param observer 回调函数接受4个参数,见类说明。 392 | */ 393 | __proto.on=function(type,caller,listener,args){ 394 | _super.prototype.on.call(this,type,caller,listener,args); 395 | Browser.window.addEventListener('devicemotion',this.onDeviceOrientationChange); 396 | return this; 397 | } 398 | 399 | /** 400 | *取消侦听加速器。 401 | *@param handle 侦听加速器所用处理器。 402 | */ 403 | __proto.off=function(type,caller,listener,onceOnly){ 404 | (onceOnly===void 0)&& (onceOnly=false); 405 | if (!this.hasListener(type)) 406 | Browser.window.removeEventListener('devicemotion',this.onDeviceOrientationChange) 407 | return _super.prototype.off.call(this,type,caller,listener,onceOnly); 408 | } 409 | 410 | __proto.onDeviceOrientationChange=function(e){ 411 | var interval=e.interval; 412 | Accelerator.acceleration.x=e.acceleration.x; 413 | Accelerator.acceleration.y=e.acceleration.y; 414 | Accelerator.acceleration.z=e.acceleration.z; 415 | Accelerator.accelerationIncludingGravity.x=e.accelerationIncludingGravity.x; 416 | Accelerator.accelerationIncludingGravity.y=e.accelerationIncludingGravity.y; 417 | Accelerator.accelerationIncludingGravity.z=e.accelerationIncludingGravity.z; 418 | Accelerator.rotationRate.alpha=e.rotationRate.gamma *-1; 419 | Accelerator.rotationRate.beta=e.rotationRate.alpha *-1; 420 | Accelerator.rotationRate.gamma=e.rotationRate.beta; 421 | if (Browser.onAndroid){ 422 | if (Accelerator.onChrome){ 423 | Accelerator.rotationRate.alpha *=180 / Math.PI; 424 | Accelerator.rotationRate.beta *=180 / Math.PI; 425 | Accelerator.rotationRate.gamma *=180 / Math.PI; 426 | } 427 | Accelerator.acceleration.x *=-1; 428 | Accelerator.accelerationIncludingGravity.x *=-1; 429 | } 430 | else if (Browser.onIOS){ 431 | Accelerator.acceleration.y *=-1; 432 | Accelerator.acceleration.z *=-1; 433 | Accelerator.accelerationIncludingGravity.y *=-1; 434 | Accelerator.accelerationIncludingGravity.z *=-1; 435 | interval *=1000; 436 | } 437 | this.event(/*laya.events.Event.CHANGE*/"change",[Accelerator.acceleration,Accelerator.accelerationIncludingGravity,Accelerator.rotationRate,interval]); 438 | } 439 | 440 | __getset(1,Accelerator,'instance',function(){Accelerator._instance=Accelerator._instance|| new Accelerator(0) 441 | return Accelerator._instance; 442 | },laya.events.EventDispatcher._$SET_instance); 443 | 444 | Accelerator.getTransformedAcceleration=function(acceleration){Accelerator.transformedAcceleration=Accelerator.transformedAcceleration|| new AccelerationInfo(); 445 | Accelerator.transformedAcceleration.z=acceleration.z; 446 | if (Browser.window.orientation==90){ 447 | Accelerator.transformedAcceleration.x=acceleration.y; 448 | Accelerator.transformedAcceleration.y=-acceleration.x; 449 | } 450 | else if (Browser.window.orientation==-90){ 451 | Accelerator.transformedAcceleration.x=-acceleration.y; 452 | Accelerator.transformedAcceleration.y=acceleration.x; 453 | } 454 | else if (!Browser.window.orientation){ 455 | Accelerator.transformedAcceleration.x=acceleration.x; 456 | Accelerator.transformedAcceleration.y=acceleration.y; 457 | } 458 | else if (Browser.window.orientation==180){ 459 | Accelerator.transformedAcceleration.x=-acceleration.x; 460 | Accelerator.transformedAcceleration.y=-acceleration.y; 461 | }; 462 | var tx=NaN; 463 | if (Laya.stage.canvasDegree==-90){ 464 | tx=Accelerator.transformedAcceleration.x; 465 | Accelerator.transformedAcceleration.x=-Accelerator.transformedAcceleration.y; 466 | Accelerator.transformedAcceleration.y=tx; 467 | } 468 | else if (Laya.stage.canvasDegree==90){ 469 | tx=Accelerator.transformedAcceleration.x; 470 | Accelerator.transformedAcceleration.x=Accelerator.transformedAcceleration.y; 471 | Accelerator.transformedAcceleration.y=-tx; 472 | } 473 | return Accelerator.transformedAcceleration; 474 | } 475 | 476 | Accelerator._instance=null; 477 | Accelerator.transformedAcceleration=null; 478 | __static(Accelerator, 479 | ['acceleration',function(){return this.acceleration=new AccelerationInfo();},'accelerationIncludingGravity',function(){return this.accelerationIncludingGravity=new AccelerationInfo();},'rotationRate',function(){return this.rotationRate=new RotationInfo();},'onChrome',function(){return this.onChrome=(Browser.userAgent.indexOf("Chrome")>-1);} 480 | ]); 481 | return Accelerator; 482 | })(EventDispatcher) 483 | 484 | 485 | /** 486 | *Video将视频显示到Canvas上。Video可能不会在所有浏览器有效。 487 | *

关于Video支持的所有事件参见:http://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp

488 | *

489 | *注意:
490 | *在PC端可以在任何时机调用play()因此,可以在程序开始运行时就使Video开始播放。但是在移动端,只有在用户第一次触碰屏幕后才可以调用play(),所以移动端不可能在程序开始运行时就自动开始播放Video。 491 | *

492 | * 493 | *

MDN Video链接: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video

494 | */ 495 | //class laya.device.media.Video extends laya.display.Sprite 496 | var Video=(function(_super){ 497 | function Video(width,height){ 498 | this.htmlVideo=null; 499 | this.videoElement=null; 500 | this.internalTexture=null; 501 | (width===void 0)&& (width=320); 502 | (height===void 0)&& (height=240); 503 | Video.__super.call(this); 504 | this.htmlVideo=new WebGLVideo(); 505 | this.videoElement=this.htmlVideo.getVideo(); 506 | this.videoElement.layaTarget=this; 507 | this.internalTexture=new Texture(this.htmlVideo); 508 | this.videoElement.addEventListener("abort",Video.onAbort); 509 | this.videoElement.addEventListener("canplay",Video.onCanplay); 510 | this.videoElement.addEventListener("canplaythrough",Video.onCanplaythrough); 511 | this.videoElement.addEventListener("durationchange",Video.onDurationchange); 512 | this.videoElement.addEventListener("emptied",Video.onEmptied); 513 | this.videoElement.addEventListener("error",Video.onError); 514 | this.videoElement.addEventListener("loadeddata",Video.onLoadeddata); 515 | this.videoElement.addEventListener("loadedmetadata",Video.onLoadedmetadata); 516 | this.videoElement.addEventListener("loadstart",Video.onLoadstart); 517 | this.videoElement.addEventListener("pause",Video.onPause); 518 | this.videoElement.addEventListener("play",Video.onPlay); 519 | this.videoElement.addEventListener("playing",Video.onPlaying); 520 | this.videoElement.addEventListener("progress",Video.onProgress); 521 | this.videoElement.addEventListener("ratechange",Video.onRatechange); 522 | this.videoElement.addEventListener("seeked",Video.onSeeked); 523 | this.videoElement.addEventListener("seeking",Video.onSeeking); 524 | this.videoElement.addEventListener("stalled",Video.onStalled); 525 | this.videoElement.addEventListener("suspend",Video.onSuspend); 526 | this.videoElement.addEventListener("timeupdate",Video.onTimeupdate); 527 | this.videoElement.addEventListener("volumechange",Video.onVolumechange); 528 | this.videoElement.addEventListener("waiting",Video.onWaiting); 529 | this.videoElement.addEventListener("ended",this.onPlayComplete['bind'](this)); 530 | this.size(width,height); 531 | if (Browser.onMobile){ 532 | /*__JS__ */this.onDocumentClick=this.onDocumentClick.bind(this); 533 | Browser.document.addEventListener("touchend",this.onDocumentClick); 534 | } 535 | } 536 | 537 | __class(Video,'laya.device.media.Video',_super); 538 | var __proto=Video.prototype; 539 | __proto.onPlayComplete=function(e){ 540 | this.event("ended"); 541 | if(!Render.isConchApp || !this.videoElement || !this.videoElement.loop) 542 | Laya.timer.clear(this,this.renderCanvas); 543 | } 544 | 545 | /** 546 | *设置播放源。 547 | *@param url 播放源路径。 548 | */ 549 | __proto.load=function(url){ 550 | if (url.indexOf("blob:")==0) 551 | this.videoElement.src=url; 552 | else 553 | this.htmlVideo.setSource(url,laya.device.media.Video.MP4); 554 | } 555 | 556 | /** 557 | *开始播放视频。 558 | */ 559 | __proto.play=function(){ 560 | this.videoElement.play(); 561 | Laya.timer.frameLoop(1,this,this.renderCanvas); 562 | } 563 | 564 | /** 565 | *暂停视频播放。 566 | */ 567 | __proto.pause=function(){ 568 | this.videoElement.pause(); 569 | Laya.timer.clear(this,this.renderCanvas); 570 | } 571 | 572 | /** 573 | *重新加载视频。 574 | */ 575 | __proto.reload=function(){ 576 | this.videoElement.load(); 577 | } 578 | 579 | /** 580 | *检测是否支持播放指定格式视频。 581 | *@param type 参数为Video.MP4 / Video.OGG / Video.WEBM之一。 582 | *@return 表示支持的级别。可能的值: 583 | * 588 | */ 589 | __proto.canPlayType=function(type){ 590 | var typeString; 591 | switch (type){ 592 | case laya.device.media.Video.MP4: 593 | typeString="video/mp4"; 594 | break ; 595 | case laya.device.media.Video.OGG: 596 | typeString="video/ogg"; 597 | break ; 598 | case laya.device.media.Video.WEBM: 599 | typeString="video/webm"; 600 | break ; 601 | } 602 | return this.videoElement.canPlayType(typeString); 603 | } 604 | 605 | __proto.renderCanvas=function(){ 606 | if (this.readyState===0) 607 | return; 608 | this.htmlVideo['updateTexture'](); 609 | this.graphics.clear(); 610 | this.graphics.drawTexture(this.internalTexture,0,0,this.width,this.height); 611 | } 612 | 613 | __proto.onDocumentClick=function(){ 614 | this.videoElement.play(); 615 | this.videoElement.pause(); 616 | Browser.document.removeEventListener("touchend",this.onDocumentClick); 617 | } 618 | 619 | __proto.size=function(width,height){ 620 | _super.prototype.size.call(this,width,height) 621 | if (Render.isConchApp){ 622 | var transform=Utils.getTransformRelativeToWindow(this,0,0); 623 | this.videoElement.width=width *transform.scaleX; 624 | } 625 | else{ 626 | this.videoElement.width=width / Browser.pixelRatio; 627 | } 628 | if (this.paused)this.renderCanvas(); 629 | return this; 630 | } 631 | 632 | /** 633 | *销毁内部事件绑定。 634 | */ 635 | __proto.destroy=function(detroyChildren){ 636 | (detroyChildren===void 0)&& (detroyChildren=true); 637 | _super.prototype.destroy.call(this,detroyChildren); 638 | this.videoElement.removeEventListener("abort",Video.onAbort); 639 | this.videoElement.removeEventListener("canplay",Video.onCanplay); 640 | this.videoElement.removeEventListener("canplaythrough",Video.onCanplaythrough); 641 | this.videoElement.removeEventListener("durationchange",Video.onDurationchange); 642 | this.videoElement.removeEventListener("emptied",Video.onEmptied); 643 | this.videoElement.removeEventListener("error",Video.onError); 644 | this.videoElement.removeEventListener("loadeddata",Video.onLoadeddata); 645 | this.videoElement.removeEventListener("loadedmetadata",Video.onLoadedmetadata); 646 | this.videoElement.removeEventListener("loadstart",Video.onLoadstart); 647 | this.videoElement.removeEventListener("pause",Video.onPause); 648 | this.videoElement.removeEventListener("play",Video.onPlay); 649 | this.videoElement.removeEventListener("playing",Video.onPlaying); 650 | this.videoElement.removeEventListener("progress",Video.onProgress); 651 | this.videoElement.removeEventListener("ratechange",Video.onRatechange); 652 | this.videoElement.removeEventListener("seeked",Video.onSeeked); 653 | this.videoElement.removeEventListener("seeking",Video.onSeeking); 654 | this.videoElement.removeEventListener("stalled",Video.onStalled); 655 | this.videoElement.removeEventListener("suspend",Video.onSuspend); 656 | this.videoElement.removeEventListener("timeupdate",Video.onTimeupdate); 657 | this.videoElement.removeEventListener("volumechange",Video.onVolumechange); 658 | this.videoElement.removeEventListener("waiting",Video.onWaiting); 659 | this.videoElement.removeEventListener("ended",this.onPlayComplete); 660 | this.pause(); 661 | this.videoElement.layaTarget=null 662 | this.videoElement=null; 663 | this.htmlVideo.destroy(); 664 | } 665 | 666 | __proto.syncVideoPosition=function(){ 667 | var stage=Laya.stage; 668 | var rec; 669 | rec=Utils.getGlobalPosAndScale(this); 670 | var a=stage._canvasTransform.a,d=stage._canvasTransform.d; 671 | var x=rec.x *stage.clientScaleX *a+stage.offset.x; 672 | var y=rec.y *stage.clientScaleY *d+stage.offset.y; 673 | this.videoElement.style.left=x+'px';; 674 | this.videoElement.style.top=y+'px'; 675 | this.videoElement.width=this.width / Browser.pixelRatio; 676 | this.videoElement.height=this.height / Browser.pixelRatio; 677 | } 678 | 679 | /** 680 | *buffered 属性返回 TimeRanges(JS)对象。TimeRanges 对象表示用户的音视频缓冲范围。缓冲范围指的是已缓冲音视频的时间范围。如果用户在音视频中跳跃播放,会得到多个缓冲范围。 681 | *

buffered.length返回缓冲范围个数。如获取第一个缓冲范围则是buffered.start(0)和buffered.end(0)。以秒计。

682 | *@return TimeRanges(JS)对象 683 | */ 684 | __getset(0,__proto,'buffered',function(){ 685 | return this.videoElement.buffered; 686 | }); 687 | 688 | /** 689 | *获取视频源尺寸。ready事件触发后可用。 690 | */ 691 | __getset(0,__proto,'videoWidth',function(){ 692 | return this.videoElement.videoWidth; 693 | }); 694 | 695 | /** 696 | *获取当前播放源路径。 697 | */ 698 | __getset(0,__proto,'currentSrc',function(){ 699 | return this.videoElement.currentSrc; 700 | }); 701 | 702 | /** 703 | *设置和获取当前播放头位置。 704 | */ 705 | __getset(0,__proto,'currentTime',function(){ 706 | return this.videoElement.currentTime; 707 | },function(value){ 708 | this.videoElement.currentTime=value; 709 | this.renderCanvas(); 710 | }); 711 | 712 | /** 713 | *返回音频/视频的播放是否已结束 714 | */ 715 | __getset(0,__proto,'ended',function(){ 716 | return this.videoElement.ended; 717 | }); 718 | 719 | /** 720 | *设置和获取当前音量。 721 | */ 722 | __getset(0,__proto,'volume',function(){ 723 | return this.videoElement.volume; 724 | },function(value){ 725 | this.videoElement.volume=value; 726 | }); 727 | 728 | __getset(0,__proto,'videoHeight',function(){ 729 | return this.videoElement.videoHeight; 730 | }); 731 | 732 | /** 733 | *表示视频元素的就绪状态: 734 | * 741 | */ 742 | __getset(0,__proto,'readyState',function(){ 743 | return this.videoElement.readyState; 744 | }); 745 | 746 | /** 747 | *获取视频长度(秒)。ready事件触发后可用。 748 | */ 749 | __getset(0,__proto,'duration',function(){ 750 | return this.videoElement.duration; 751 | }); 752 | 753 | /** 754 | *返回表示音频/视频错误状态的 MediaError(JS)对象。 755 | */ 756 | __getset(0,__proto,'error',function(){ 757 | return this.videoElement.error; 758 | }); 759 | 760 | /** 761 | *设置或返回音频/视频是否应在结束时重新播放。 762 | */ 763 | __getset(0,__proto,'loop',function(){ 764 | return this.videoElement.loop; 765 | },function(value){ 766 | this.videoElement.loop=value; 767 | }); 768 | 769 | /** 770 | *设置视频的x坐标 771 | */ 772 | __getset(0,__proto,'x',_super.prototype._$get_x,function(val){ 773 | Laya.superSet(Sprite,this,'x',val); 774 | if (Render.isConchApp){ 775 | var transform=Utils.getTransformRelativeToWindow(this,0,0); 776 | this.videoElement.style.left=transform.x; 777 | } 778 | }); 779 | 780 | /** 781 | *设置视频的y坐标 782 | */ 783 | __getset(0,__proto,'y',_super.prototype._$get_y,function(val){ 784 | Laya.superSet(Sprite,this,'y',val); 785 | if (Render.isConchApp){ 786 | var transform=Utils.getTransformRelativeToWindow(this,0,0); 787 | this.videoElement.style.top=transform.y; 788 | } 789 | }); 790 | 791 | /** 792 | *playbackRate 属性设置或返回音频/视频的当前播放速度。如: 793 | * 800 | *

只有 Google Chrome 和 Safari 支持 playbackRate 属性。

801 | */ 802 | __getset(0,__proto,'playbackRate',function(){ 803 | return this.videoElement.playbackRate; 804 | },function(value){ 805 | this.videoElement.playbackRate=value; 806 | }); 807 | 808 | /** 809 | *获取和设置静音状态。 810 | */ 811 | __getset(0,__proto,'muted',function(){ 812 | return this.videoElement.muted; 813 | },function(value){ 814 | this.videoElement.muted=value; 815 | }); 816 | 817 | /** 818 | *返回视频是否暂停 819 | */ 820 | __getset(0,__proto,'paused',function(){ 821 | return this.videoElement.paused; 822 | }); 823 | 824 | /** 825 | *preload 属性设置或返回是否在页面加载后立即加载视频。可赋值如下: 826 | * 831 | */ 832 | __getset(0,__proto,'preload',function(){ 833 | return this.videoElement.preload; 834 | },function(value){ 835 | this.videoElement.preload=value; 836 | }); 837 | 838 | /** 839 | *参见 http://www.w3school.com.cn/tags/av_prop_seekable.asp。 840 | */ 841 | __getset(0,__proto,'seekable',function(){ 842 | return this.videoElement.seekable; 843 | }); 844 | 845 | /** 846 | *seeking 属性返回用户目前是否在音频/视频中寻址。 847 | *寻址中(Seeking)指的是用户在音频/视频中移动/跳跃到新的位置。 848 | */ 849 | __getset(0,__proto,'seeking',function(){ 850 | return this.videoElement.seeking; 851 | }); 852 | 853 | __getset(0,__proto,'width',_super.prototype._$get_width,function(value){ 854 | if (Render.isConchApp){ 855 | var transform=Utils.getTransformRelativeToWindow(this,0,0); 856 | this.videoElement.width=value *transform.scaleX; 857 | } 858 | else{ 859 | this.videoElement.width=this.width / Browser.pixelRatio; 860 | } 861 | Laya.superSet(Sprite,this,'width',value); 862 | if (this.paused)this.renderCanvas(); 863 | }); 864 | 865 | __getset(0,__proto,'height',_super.prototype._$get_height,function(value){ 866 | if (Render.isConchApp){ 867 | var transform=Utils.getTransformRelativeToWindow(this,0,0); 868 | this.videoElement.height=value *transform.scaleY; 869 | } 870 | else{ 871 | this.videoElement.height=this.height / Browser.pixelRatio; 872 | } 873 | Laya.superSet(Sprite,this,'height',value); 874 | }); 875 | 876 | Video.onAbort=function(e){e.target.layaTarget.event("abort")} 877 | Video.onCanplay=function(e){e.target.layaTarget.event("canplay")} 878 | Video.onCanplaythrough=function(e){e.target.layaTarget.event("canplaythrough")} 879 | Video.onDurationchange=function(e){e.target.layaTarget.event("durationchange")} 880 | Video.onEmptied=function(e){e.target.layaTarget.event("emptied")} 881 | Video.onError=function(e){e.target.layaTarget.event("error")} 882 | Video.onLoadeddata=function(e){e.target.layaTarget.event("loadeddata")} 883 | Video.onLoadedmetadata=function(e){e.target.layaTarget.event("loadedmetadata")} 884 | Video.onLoadstart=function(e){e.target.layaTarget.event("loadstart")} 885 | Video.onPause=function(e){e.target.layaTarget.event("pause")} 886 | Video.onPlay=function(e){e.target.layaTarget.event("play")} 887 | Video.onPlaying=function(e){e.target.layaTarget.event("playing")} 888 | Video.onProgress=function(e){e.target.layaTarget.event("progress")} 889 | Video.onRatechange=function(e){e.target.layaTarget.event("ratechange")} 890 | Video.onSeeked=function(e){e.target.layaTarget.event("seeked")} 891 | Video.onSeeking=function(e){e.target.layaTarget.event("seeking")} 892 | Video.onStalled=function(e){e.target.layaTarget.event("stalled")} 893 | Video.onSuspend=function(e){e.target.layaTarget.event("suspend")} 894 | Video.onTimeupdate=function(e){e.target.layaTarget.event("timeupdate")} 895 | Video.onVolumechange=function(e){e.target.layaTarget.event("volumechange")} 896 | Video.onWaiting=function(e){e.target.layaTarget.event("waiting")} 897 | Video.MP4=1; 898 | Video.OGG=2; 899 | Video.CAMERA=4; 900 | Video.WEBM=8; 901 | Video.SUPPORT_PROBABLY="probably"; 902 | Video.SUPPORT_MAYBY="maybe"; 903 | Video.SUPPORT_NO=""; 904 | return Video; 905 | })(Sprite) 906 | 907 | 908 | /** 909 | *@private 910 | */ 911 | //class laya.device.media.HtmlVideo extends laya.resource.Bitmap 912 | var HtmlVideo=(function(_super){ 913 | function HtmlVideo(){ 914 | this.video=null; 915 | this._source=null; 916 | HtmlVideo.__super.call(this); 917 | this._width=1; 918 | this._height=1; 919 | this.createDomElement(); 920 | } 921 | 922 | __class(HtmlVideo,'laya.device.media.HtmlVideo',_super); 923 | var __proto=HtmlVideo.prototype; 924 | __proto.createDomElement=function(){ 925 | var _$this=this; 926 | this._source=this.video=Browser.createElement("video"); 927 | var style=this.video.style; 928 | style.position='absolute'; 929 | style.top='0px'; 930 | style.left='0px'; 931 | this.video.addEventListener("loadedmetadata",(function(){ 932 | this._w=_$this.video.videoWidth; 933 | this._h=_$this.video.videoHeight; 934 | })['bind'](this)); 935 | } 936 | 937 | __proto.setSource=function(url,extension){ 938 | while(this.video.childElementCount) 939 | this.video.firstChild.remove(); 940 | if (extension & Video.MP4) 941 | this.appendSource(url,"video/mp4"); 942 | if (extension & Video.OGG) 943 | this.appendSource(url+".ogg","video/ogg"); 944 | } 945 | 946 | __proto.appendSource=function(source,type){ 947 | var sourceElement=Browser.createElement("source"); 948 | sourceElement.src=source; 949 | sourceElement.type=type; 950 | this.video.appendChild(sourceElement); 951 | } 952 | 953 | __proto.getVideo=function(){ 954 | return this.video; 955 | } 956 | 957 | __proto._getSource=function(){ 958 | return this._source; 959 | } 960 | 961 | __proto.destroy=function(){ 962 | laya.resource.Resource.prototype.destroy.call(this); 963 | var isConchApp=/*__JS__ */Render.isConchApp; 964 | if (isConchApp){ 965 | this.video._destroy(); 966 | } 967 | } 968 | 969 | HtmlVideo.create=function(){ 970 | return new HtmlVideo(); 971 | } 972 | 973 | return HtmlVideo; 974 | })(Bitmap) 975 | 976 | 977 | /** 978 | *@private 979 | */ 980 | //class laya.device.media.WebGLVideo extends laya.device.media.HtmlVideo 981 | var WebGLVideo=(function(_super){ 982 | function WebGLVideo(){ 983 | this.gl=null; 984 | this.preTarget=null; 985 | this.preTexture=null; 986 | WebGLVideo.__super.call(this); 987 | if(!Render.isConchApp && Browser.onIPhone) 988 | return; 989 | this.gl=/*__JS__ */Render.isConchApp ? LayaGLContext.instance :WebGL.mainContext; 990 | this._source=this.gl.createTexture(); 991 | WebGLContext.bindTexture(this.gl,/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,this._source); 992 | this.gl.texParameteri(/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,/*laya.webgl.WebGLContext.TEXTURE_WRAP_S*/0x2802,/*laya.webgl.WebGLContext.CLAMP_TO_EDGE*/0x812F); 993 | this.gl.texParameteri(/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,/*laya.webgl.WebGLContext.TEXTURE_WRAP_T*/0x2803,/*laya.webgl.WebGLContext.CLAMP_TO_EDGE*/0x812F); 994 | this.gl.texParameteri(/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,/*laya.webgl.WebGLContext.TEXTURE_MAG_FILTER*/0x2800,/*laya.webgl.WebGLContext.LINEAR*/0x2601); 995 | this.gl.texParameteri(/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,/*laya.webgl.WebGLContext.TEXTURE_MIN_FILTER*/0x2801,/*laya.webgl.WebGLContext.LINEAR*/0x2601); 996 | WebGLContext.bindTexture(this.gl,/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,null); 997 | } 998 | 999 | __class(WebGLVideo,'laya.device.media.WebGLVideo',_super); 1000 | var __proto=WebGLVideo.prototype; 1001 | //(preTarget && preTexture)&& (WebGLContext.bindTexture(gl,preTarget,preTexture)); 1002 | __proto.updateTexture=function(){ 1003 | if(!Render.isConchApp && Browser.onIPhone) 1004 | return; 1005 | WebGLContext.bindTexture(this.gl,/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,this._source); 1006 | this.gl.texImage2D(/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,0,/*laya.webgl.WebGLContext.RGB*/0x1907,/*laya.webgl.WebGLContext.RGB*/0x1907,/*laya.webgl.WebGLContext.UNSIGNED_BYTE*/0x1401,this.video); 1007 | WebGLVideo.curBindSource=this._source; 1008 | } 1009 | 1010 | __proto.destroy=function(){ 1011 | if (this._source){ 1012 | this.gl=/*__JS__ */Render.isConchApp ? LayaGLContext.instance :WebGL.mainContext; 1013 | if (WebGLVideo.curBindSource==this._source){ 1014 | WebGLContext.bindTexture(this.gl,/*laya.webgl.WebGLContext.TEXTURE_2D*/0x0DE1,null); 1015 | WebGLVideo.curBindSource=null; 1016 | } 1017 | this.gl.deleteTexture(this._source); 1018 | } 1019 | _super.prototype.destroy.call(this); 1020 | } 1021 | 1022 | __getset(0,__proto,'_glTexture',function(){ 1023 | return this._source; 1024 | }); 1025 | 1026 | WebGLVideo.curBindSource=null; 1027 | return WebGLVideo; 1028 | })(HtmlVideo) 1029 | 1030 | 1031 | Laya.__init([Media]); 1032 | })(window,document,Laya); 1033 | -------------------------------------------------------------------------------- /bin/libs/laya.effect.js: -------------------------------------------------------------------------------- 1 | 2 | (function(window,document,Laya){ 3 | var __un=Laya.un,__uns=Laya.uns,__static=Laya.static,__class=Laya.class,__getset=Laya.getset,__newvec=Laya.__newvec; 4 | 5 | var BlurFilter=laya.filters.BlurFilter,ColorFilter=laya.filters.ColorFilter,ColorUtils=laya.utils.ColorUtils; 6 | var Component=laya.components.Component,Ease=laya.utils.Ease,Event=laya.events.Event,GlowFilter=laya.filters.GlowFilter; 7 | var Handler=laya.utils.Handler,Node=laya.display.Node,Sprite=laya.display.Sprite,Tween=laya.utils.Tween,Utils=laya.utils.Utils; 8 | /** 9 | *... 10 | *@author ww 11 | */ 12 | //class laya.effect.FilterSetterBase 13 | var FilterSetterBase=(function(){ 14 | function FilterSetterBase(){ 15 | this._filter=null; 16 | this._target=null; 17 | } 18 | 19 | __class(FilterSetterBase,'laya.effect.FilterSetterBase'); 20 | var __proto=FilterSetterBase.prototype; 21 | __proto.paramChanged=function(){ 22 | Laya.systemTimer.callLater(this,this.buildFilter); 23 | } 24 | 25 | __proto.buildFilter=function(){ 26 | if (this._target){ 27 | this.addFilter(this._target); 28 | } 29 | } 30 | 31 | __proto.addFilter=function(sprite){ 32 | if (!sprite)return; 33 | if (!sprite.filters){ 34 | sprite.filters=[this._filter]; 35 | }else{ 36 | var preFilters; 37 | preFilters=sprite.filters; 38 | if (preFilters.indexOf(this._filter)< 0){ 39 | preFilters.push(this._filter); 40 | sprite.filters=Utils.copyArray([],preFilters); 41 | } 42 | } 43 | } 44 | 45 | __proto.removeFilter=function(sprite){ 46 | if (!sprite)return; 47 | sprite.filters=null; 48 | } 49 | 50 | __getset(0,__proto,'target',null,function(value){ 51 | if (this._target !=value){ 52 | this._target=value; 53 | this.paramChanged(); 54 | } 55 | }); 56 | 57 | return FilterSetterBase; 58 | })() 59 | 60 | 61 | /** 62 | *@Script {name:ButtonEffect} 63 | *@author ww 64 | */ 65 | //class laya.effect.ButtonEffect 66 | var ButtonEffect=(function(){ 67 | function ButtonEffect(){ 68 | this._tar=null; 69 | this._curState=0; 70 | this._curTween=null; 71 | /** 72 | *effectScale 73 | *@prop {name:effectScale,type:number,tips:"缩放值",default:"1.5"} 74 | */ 75 | this.effectScale=1.5; 76 | /** 77 | *tweenTime 78 | *@prop {name:tweenTime,type:number,tips:"缓动时长",default:"300"} 79 | */ 80 | this.tweenTime=300; 81 | /** 82 | *effectEase 83 | *@prop {name:effectEase,type:ease,tips:"效果缓动类型"} 84 | */ 85 | this.effectEase=null; 86 | /** 87 | *backEase 88 | *@prop {name:backEase,type:ease,tips:"恢复缓动类型"} 89 | */ 90 | this.backEase=null; 91 | } 92 | 93 | __class(ButtonEffect,'laya.effect.ButtonEffect'); 94 | var __proto=ButtonEffect.prototype; 95 | __proto.toChangedState=function(){ 96 | this._curState=1; 97 | if (this._curTween)Tween.clear(this._curTween); 98 | this._curTween=Tween.to(this._tar,{scaleX:this.effectScale,scaleY:this.effectScale },this.tweenTime,Ease[this.effectEase],Handler.create(this,this.tweenComplete)); 99 | } 100 | 101 | __proto.toInitState=function(){ 102 | if (this._curState==2)return; 103 | if (this._curTween)Tween.clear(this._curTween); 104 | this._curState=2; 105 | this._curTween=Tween.to(this._tar,{scaleX:1,scaleY:1 },this.tweenTime,Ease[this.backEase],Handler.create(this,this.tweenComplete)); 106 | } 107 | 108 | __proto.tweenComplete=function(){ 109 | this._curState=0; 110 | this._curTween=null; 111 | } 112 | 113 | /** 114 | *设置控制对象 115 | *@param tar 116 | */ 117 | __getset(0,__proto,'target',null,function(tar){ 118 | this._tar=tar; 119 | tar.on(/*laya.events.Event.MOUSE_DOWN*/"mousedown",this,this.toChangedState); 120 | tar.on(/*laya.events.Event.MOUSE_UP*/"mouseup",this,this.toInitState); 121 | tar.on(/*laya.events.Event.MOUSE_OUT*/"mouseout",this,this.toInitState); 122 | }); 123 | 124 | return ButtonEffect; 125 | })() 126 | 127 | 128 | /** 129 | *效果插件基类,基于对象池管理 130 | */ 131 | //class laya.effect.EffectBase extends laya.components.Component 132 | var EffectBase=(function(_super){ 133 | function EffectBase(){ 134 | /**动画持续时间,单位为毫秒*/ 135 | this.duration=1000; 136 | /**动画延迟时间,单位为毫秒*/ 137 | this.delay=0; 138 | /**重复次数,默认为播放一次*/ 139 | this.repeat=0; 140 | /**缓动类型,如果为空,则默认为匀速播放*/ 141 | this.ease=null; 142 | /**触发事件,如果为空,则创建时触发*/ 143 | this.eventName=null; 144 | /**效用作用的目标对象,如果为空,则是脚本所在的节点本身*/ 145 | this.target=null; 146 | /**效果结束后,是否自动移除节点*/ 147 | this.autoDestroyAtComplete=true; 148 | this._comlete=null; 149 | this._tween=null; 150 | EffectBase.__super.call(this); 151 | } 152 | 153 | __class(EffectBase,'laya.effect.EffectBase',_super); 154 | var __proto=EffectBase.prototype; 155 | __proto._onAwake=function(){this.target=this.target|| this.owner; 156 | if (this.autoDestroyAtComplete)this._comlete=Handler.create(this.target,this.target.destroy,null,false); 157 | if (this.eventName)this.owner.on(this.eventName,this,this._exeTween); 158 | else this._exeTween(); 159 | } 160 | 161 | __proto._exeTween=function(){ 162 | this._tween=this._doTween(); 163 | this._tween.repeat=this.repeat; 164 | } 165 | 166 | __proto._doTween=function(){ 167 | return null; 168 | } 169 | 170 | __proto.onReset=function(){ 171 | this.duration=1000; 172 | this.delay=0; 173 | this.repeat=0; 174 | this.ease=null; 175 | this.target=null; 176 | if (this.eventName){ 177 | this.owner.off(this.eventName,this,this._exeTween); 178 | this.eventName=null; 179 | } 180 | if (this._comlete){ 181 | this._comlete.recover(); 182 | this._comlete=null; 183 | } 184 | if (this._tween){ 185 | this._tween.clear(); 186 | this._tween=null; 187 | } 188 | } 189 | 190 | return EffectBase; 191 | })(Component) 192 | 193 | 194 | /** 195 | *... 196 | *@author ww 197 | */ 198 | //class laya.effect.ColorFilterSetter extends laya.effect.FilterSetterBase 199 | var ColorFilterSetter=(function(_super){ 200 | function ColorFilterSetter(){ 201 | /** 202 | *brightness 亮度,范围:-100~100 203 | */ 204 | this._brightness=0; 205 | /** 206 | *contrast 对比度,范围:-100~100 207 | */ 208 | this._contrast=0; 209 | /** 210 | *saturation 饱和度,范围:-100~100 211 | */ 212 | this._saturation=0; 213 | /** 214 | *hue 色调,范围:-180~180 215 | */ 216 | this._hue=0; 217 | /** 218 | *red red增量,范围:0~255 219 | */ 220 | this._red=0; 221 | /** 222 | *green green增量,范围:0~255 223 | */ 224 | this._green=0; 225 | /** 226 | *blue blue增量,范围:0~255 227 | */ 228 | this._blue=0; 229 | /** 230 | *alpha alpha增量,范围:0~255 231 | */ 232 | this._alpha=0; 233 | this._color=null; 234 | ColorFilterSetter.__super.call(this); 235 | this._filter=new ColorFilter(); 236 | } 237 | 238 | __class(ColorFilterSetter,'laya.effect.ColorFilterSetter',_super); 239 | var __proto=ColorFilterSetter.prototype; 240 | __proto.buildFilter=function(){ 241 | this._filter.reset(); 242 | this._filter.color(this.red,this.green,this.blue,this.alpha); 243 | this._filter.adjustHue(this.hue); 244 | this._filter.adjustContrast(this.contrast); 245 | this._filter.adjustBrightness(this.brightness); 246 | this._filter.adjustSaturation(this.saturation); 247 | _super.prototype.buildFilter.call(this); 248 | } 249 | 250 | __getset(0,__proto,'brightness',function(){ 251 | return this._brightness; 252 | },function(value){ 253 | this._brightness=value; 254 | this.paramChanged(); 255 | }); 256 | 257 | __getset(0,__proto,'alpha',function(){ 258 | return this._alpha; 259 | },function(value){ 260 | this._alpha=value; 261 | this.paramChanged(); 262 | }); 263 | 264 | __getset(0,__proto,'contrast',function(){ 265 | return this._contrast; 266 | },function(value){ 267 | this._contrast=value; 268 | this.paramChanged(); 269 | }); 270 | 271 | __getset(0,__proto,'hue',function(){ 272 | return this._hue; 273 | },function(value){ 274 | this._hue=value; 275 | this.paramChanged(); 276 | }); 277 | 278 | __getset(0,__proto,'saturation',function(){ 279 | return this._saturation; 280 | },function(value){ 281 | this._saturation=value; 282 | this.paramChanged(); 283 | }); 284 | 285 | __getset(0,__proto,'green',function(){ 286 | return this._green; 287 | },function(value){ 288 | this._green=value; 289 | this.paramChanged(); 290 | }); 291 | 292 | __getset(0,__proto,'red',function(){ 293 | return this._red; 294 | },function(value){ 295 | this._red=value; 296 | this.paramChanged(); 297 | }); 298 | 299 | __getset(0,__proto,'blue',function(){ 300 | return this._blue; 301 | },function(value){ 302 | this._blue=value; 303 | this.paramChanged(); 304 | }); 305 | 306 | __getset(0,__proto,'color',function(){ 307 | return this._color; 308 | },function(value){ 309 | this._color=value; 310 | var colorO; 311 | colorO=ColorUtils.create(value); 312 | this._red=colorO.arrColor[0] *255; 313 | this._green=colorO.arrColor[1] *255; 314 | this._blue=colorO.arrColor[2] *255; 315 | this.paramChanged(); 316 | }); 317 | 318 | return ColorFilterSetter; 319 | })(FilterSetterBase) 320 | 321 | 322 | /** 323 | *... 324 | *@author ww 325 | */ 326 | //class laya.effect.GlowFilterSetter extends laya.effect.FilterSetterBase 327 | var GlowFilterSetter=(function(_super){ 328 | function GlowFilterSetter(){ 329 | /** 330 | *滤镜的颜色 331 | */ 332 | this._color="#ff0000"; 333 | /** 334 | *边缘模糊的大小 0~20 335 | */ 336 | this._blur=4; 337 | /** 338 | *X轴方向的偏移 339 | */ 340 | this._offX=6; 341 | /** 342 | *Y轴方向的偏移 343 | */ 344 | this._offY=6; 345 | GlowFilterSetter.__super.call(this); 346 | this._filter=new GlowFilter(this._color); 347 | } 348 | 349 | __class(GlowFilterSetter,'laya.effect.GlowFilterSetter',_super); 350 | var __proto=GlowFilterSetter.prototype; 351 | __proto.buildFilter=function(){ 352 | this._filter=new GlowFilter(this.color,this.blur,this.offX,this.offY); 353 | _super.prototype.buildFilter.call(this); 354 | } 355 | 356 | __getset(0,__proto,'blur',function(){ 357 | return this._blur; 358 | },function(value){ 359 | this._blur=value; 360 | this.paramChanged(); 361 | }); 362 | 363 | __getset(0,__proto,'color',function(){ 364 | return this._color; 365 | },function(value){ 366 | this._color=value; 367 | this.paramChanged(); 368 | }); 369 | 370 | __getset(0,__proto,'offX',function(){ 371 | return this._offX; 372 | },function(value){ 373 | this._offX=value; 374 | this.paramChanged(); 375 | }); 376 | 377 | __getset(0,__proto,'offY',function(){ 378 | return this._offY; 379 | },function(value){ 380 | this._offY=value; 381 | this.paramChanged(); 382 | }); 383 | 384 | return GlowFilterSetter; 385 | })(FilterSetterBase) 386 | 387 | 388 | /** 389 | *... 390 | *@author ww 391 | */ 392 | //class laya.effect.BlurFilterSetter extends laya.effect.FilterSetterBase 393 | var BlurFilterSetter=(function(_super){ 394 | function BlurFilterSetter(){ 395 | this._strength=4; 396 | BlurFilterSetter.__super.call(this); 397 | this._filter=new BlurFilter(this.strength); 398 | } 399 | 400 | __class(BlurFilterSetter,'laya.effect.BlurFilterSetter',_super); 401 | var __proto=BlurFilterSetter.prototype; 402 | __proto.buildFilter=function(){ 403 | this._filter=new BlurFilter(this.strength); 404 | _super.prototype.buildFilter.call(this); 405 | } 406 | 407 | __getset(0,__proto,'strength',function(){ 408 | return this._strength; 409 | },function(value){ 410 | this._strength=value; 411 | }); 412 | 413 | return BlurFilterSetter; 414 | })(FilterSetterBase) 415 | 416 | 417 | /** 418 | *淡出效果 419 | */ 420 | //class laya.effect.FadeOut extends laya.effect.EffectBase 421 | var FadeOut=(function(_super){ 422 | function FadeOut(){ 423 | FadeOut.__super.call(this);; 424 | } 425 | 426 | __class(FadeOut,'laya.effect.FadeOut',_super); 427 | var __proto=FadeOut.prototype; 428 | __proto._doTween=function(){ 429 | this.target.alpha=1; 430 | return Tween.to(this.target,{alpha:0},this.duration,Ease[this.ease],this._comlete,this.delay); 431 | } 432 | 433 | return FadeOut; 434 | })(EffectBase) 435 | 436 | 437 | /** 438 | *淡入效果 439 | */ 440 | //class laya.effect.FadeIn extends laya.effect.EffectBase 441 | var FadeIn=(function(_super){ 442 | function FadeIn(){ 443 | FadeIn.__super.call(this);; 444 | } 445 | 446 | __class(FadeIn,'laya.effect.FadeIn',_super); 447 | var __proto=FadeIn.prototype; 448 | __proto._doTween=function(){ 449 | this.target.alpha=0; 450 | return Tween.to(this.target,{alpha:1},this.duration,Ease[this.ease],this._comlete,this.delay); 451 | } 452 | 453 | return FadeIn; 454 | })(EffectBase) 455 | 456 | 457 | 458 | })(window,document,Laya); 459 | -------------------------------------------------------------------------------- /bin/libs/laya.particle.js: -------------------------------------------------------------------------------- 1 | 2 | (function(window,document,Laya){ 3 | var __un=Laya.un,__uns=Laya.uns,__static=Laya.static,__class=Laya.class,__getset=Laya.getset,__newvec=Laya.__newvec; 4 | 5 | var BlendMode=laya.webgl.canvas.BlendMode,Buffer=laya.webgl.utils.Buffer,Context=laya.resource.Context; 6 | var DrawParticleCmd=laya.display.cmd.DrawParticleCmd,Handler=laya.utils.Handler,Loader=laya.net.Loader,MathUtil=laya.maths.MathUtil; 7 | var MeshParticle2D=laya.webgl.utils.MeshParticle2D,Render=laya.renders.Render,RenderState2D=laya.webgl.utils.RenderState2D; 8 | var Shader=laya.webgl.shader.Shader,ShaderValue=laya.webgl.shader.ShaderValue,Sprite=laya.display.Sprite; 9 | var Stat=laya.utils.Stat,Texture=laya.resource.Texture,Value2D=laya.webgl.shader.d2.value.Value2D,VertexBuffer2D=laya.webgl.utils.VertexBuffer2D; 10 | var WebGL=laya.webgl.WebGL,WebGLContext=laya.webgl.WebGLContext; 11 | /** 12 | * 13 | *ParticleTemplateBase 类是粒子模板基类 14 | * 15 | */ 16 | //class laya.particle.ParticleTemplateBase 17 | var ParticleTemplateBase=(function(){ 18 | function ParticleTemplateBase(){ 19 | /** 20 | *粒子配置数据 21 | */ 22 | this.settings=null; 23 | /** 24 | *粒子贴图 25 | */ 26 | this.texture=null; 27 | } 28 | 29 | __class(ParticleTemplateBase,'laya.particle.ParticleTemplateBase'); 30 | var __proto=ParticleTemplateBase.prototype; 31 | /** 32 | *添加一个粒子 33 | *@param position 粒子位置 34 | *@param velocity 粒子速度 35 | * 36 | */ 37 | __proto.addParticleArray=function(position,velocity){} 38 | return ParticleTemplateBase; 39 | })() 40 | 41 | 42 | /** 43 | *EmitterBase 类是粒子发射器类 44 | */ 45 | //class laya.particle.emitter.EmitterBase 46 | var EmitterBase=(function(){ 47 | function EmitterBase(){ 48 | /** 49 | *积累的帧时间 50 | */ 51 | this._frameTime=0; 52 | /** 53 | *粒子发射速率 54 | */ 55 | this._emissionRate=60; 56 | /** 57 | *当前剩余发射时间 58 | */ 59 | this._emissionTime=0; 60 | /** 61 | *发射粒子最小时间间隔 62 | */ 63 | this.minEmissionTime=1 / 60; 64 | /**@private */ 65 | this._particleTemplate=null; 66 | } 67 | 68 | __class(EmitterBase,'laya.particle.emitter.EmitterBase'); 69 | var __proto=EmitterBase.prototype; 70 | /** 71 | *开始发射粒子 72 | *@param duration 发射持续的时间(秒) 73 | */ 74 | __proto.start=function(duration){ 75 | (duration===void 0)&& (duration=Number.MAX_VALUE); 76 | if (this._emissionRate !=0) 77 | this._emissionTime=duration; 78 | } 79 | 80 | /** 81 | *停止发射粒子 82 | *@param clearParticles 是否清理当前的粒子 83 | */ 84 | __proto.stop=function(){ 85 | this._emissionTime=0; 86 | } 87 | 88 | /** 89 | *清理当前的活跃粒子 90 | *@param clearTexture 是否清理贴图数据,若清除贴图数据将无法再播放 91 | */ 92 | __proto.clear=function(){ 93 | this._emissionTime=0; 94 | } 95 | 96 | /** 97 | *发射一个粒子 98 | * 99 | */ 100 | __proto.emit=function(){} 101 | /** 102 | *时钟前进 103 | *@param passedTime 前进时间 104 | * 105 | */ 106 | __proto.advanceTime=function(passedTime){ 107 | (passedTime===void 0)&& (passedTime=1); 108 | this._emissionTime-=passedTime; 109 | if (this._emissionTime < 0)return; 110 | this._frameTime+=passedTime; 111 | if (this._frameTime < this.minEmissionTime)return; 112 | while (this._frameTime > this.minEmissionTime){ 113 | this._frameTime-=this.minEmissionTime; 114 | this.emit(); 115 | } 116 | } 117 | 118 | /** 119 | *设置粒子粒子模板 120 | *@param particleTemplate 粒子模板 121 | * 122 | */ 123 | __getset(0,__proto,'particleTemplate',null,function(particleTemplate){ 124 | this._particleTemplate=particleTemplate; 125 | }); 126 | 127 | /** 128 | *设置粒子发射速率 129 | *@param emissionRate 粒子发射速率 (个/秒) 130 | */ 131 | /** 132 | *获取粒子发射速率 133 | *@return 发射速率 粒子发射速率 (个/秒) 134 | */ 135 | __getset(0,__proto,'emissionRate',function(){ 136 | return this._emissionRate; 137 | },function(_emissionRate){ 138 | if (_emissionRate <=0)return; 139 | this._emissionRate=_emissionRate; 140 | (_emissionRate > 0)&& (this.minEmissionTime=1 / _emissionRate); 141 | }); 142 | 143 | return EmitterBase; 144 | })() 145 | 146 | 147 | /** 148 | *@private 149 | */ 150 | //class laya.particle.ParticleEmitter 151 | var ParticleEmitter=(function(){ 152 | function ParticleEmitter(templet,particlesPerSecond,initialPosition){ 153 | this._templet=null; 154 | this._timeBetweenParticles=NaN; 155 | this._previousPosition=null; 156 | this._timeLeftOver=0; 157 | this._tempVelocity=new Float32Array([0,0,0]); 158 | this._tempPosition=new Float32Array([0,0,0]); 159 | this._templet=templet; 160 | this._timeBetweenParticles=1.0 / particlesPerSecond; 161 | this._previousPosition=initialPosition; 162 | } 163 | 164 | __class(ParticleEmitter,'laya.particle.ParticleEmitter'); 165 | var __proto=ParticleEmitter.prototype; 166 | __proto.update=function(elapsedTime,newPosition){ 167 | elapsedTime=elapsedTime / 1000; 168 | if (elapsedTime > 0){ 169 | MathUtil.subtractVector3(newPosition,this._previousPosition,this._tempVelocity); 170 | MathUtil.scaleVector3(this._tempVelocity,1 / elapsedTime,this._tempVelocity); 171 | var timeToSpend=this._timeLeftOver+elapsedTime; 172 | var currentTime=-this._timeLeftOver; 173 | while (timeToSpend > this._timeBetweenParticles){ 174 | currentTime+=this._timeBetweenParticles; 175 | timeToSpend-=this._timeBetweenParticles; 176 | MathUtil.lerpVector3(this._previousPosition,newPosition,currentTime / elapsedTime,this._tempPosition); 177 | this._templet.addParticleArray(this._tempPosition,this._tempVelocity); 178 | } 179 | this._timeLeftOver=timeToSpend; 180 | } 181 | this._previousPosition[0]=newPosition[0]; 182 | this._previousPosition[1]=newPosition[1]; 183 | this._previousPosition[2]=newPosition[2]; 184 | } 185 | 186 | return ParticleEmitter; 187 | })() 188 | 189 | 190 | /** 191 | *ParticleSettings 类是粒子配置数据类 192 | */ 193 | //class laya.particle.ParticleSetting 194 | var ParticleSetting=(function(){ 195 | function ParticleSetting(){ 196 | /**贴图*/ 197 | this.textureName=null; 198 | /**贴图个数,默认为1可不设置*/ 199 | this.textureCount=1; 200 | /**由于循环队列判断算法,最大饱和粒子数为maxPartices-1*/ 201 | this.maxPartices=100; 202 | /**粒子持续时间(单位:秒)*/ 203 | this.duration=1; 204 | /**如果大于0,某些粒子的持续时间会小于其他粒子,并具有随机性(单位:无)*/ 205 | this.ageAddScale=0; 206 | /**粒子受发射器速度的敏感度(需在自定义发射器中编码设置)*/ 207 | this.emitterVelocitySensitivity=1; 208 | /**最小开始尺寸(单位:2D像素、3D坐标)*/ 209 | this.minStartSize=100; 210 | /**最大开始尺寸(单位:2D像素、3D坐标)*/ 211 | this.maxStartSize=100; 212 | /**最小结束尺寸(单位:2D像素、3D坐标)*/ 213 | this.minEndSize=100; 214 | /**最大结束尺寸(单位:2D像素、3D坐标)*/ 215 | this.maxEndSize=100; 216 | /**最小水平速度(单位:2D像素、3D坐标)*/ 217 | this.minHorizontalVelocity=0; 218 | /**最大水平速度(单位:2D像素、3D坐标)*/ 219 | this.maxHorizontalVelocity=0; 220 | /**最小垂直速度(单位:2D像素、3D坐标)*/ 221 | this.minVerticalVelocity=0; 222 | /**最大垂直速度(单位:2D像素、3D坐标)*/ 223 | this.maxVerticalVelocity=0; 224 | /**等于1时粒子从出生到消亡保持一致的速度,等于0时粒子消亡时速度为0,大于1时粒子会保持加速(单位:无)*/ 225 | this.endVelocity=1; 226 | /**最小旋转速度(单位:2D弧度/秒、3D弧度/秒)*/ 227 | this.minRotateSpeed=0; 228 | /**最大旋转速度(单位:2D弧度/秒、3D弧度/秒)*/ 229 | this.maxRotateSpeed=0; 230 | /**最小开始半径(单位:2D像素、3D坐标)*/ 231 | this.minStartRadius=0; 232 | /**最大开始半径(单位:2D像素、3D坐标)*/ 233 | this.maxStartRadius=0; 234 | /**最小结束半径(单位:2D像素、3D坐标)*/ 235 | this.minEndRadius=0; 236 | /**最大结束半径(单位:2D像素、3D坐标)*/ 237 | this.maxEndRadius=0; 238 | /**最小水平开始弧度(单位:2D弧度、3D弧度)*/ 239 | this.minHorizontalStartRadian=0; 240 | /**最大水平开始弧度(单位:2D弧度、3D弧度)*/ 241 | this.maxHorizontalStartRadian=0; 242 | /**最小垂直开始弧度(单位:2D弧度、3D弧度)*/ 243 | this.minVerticalStartRadian=0; 244 | /**最大垂直开始弧度(单位:2D弧度、3D弧度)*/ 245 | this.maxVerticalStartRadian=0; 246 | /**是否使用结束弧度,false为结束时与起始弧度保持一致,true为根据minHorizontalEndRadian、maxHorizontalEndRadian、minVerticalEndRadian、maxVerticalEndRadian计算结束弧度。*/ 247 | this.useEndRadian=true; 248 | /**最小水平结束弧度(单位:2D弧度、3D弧度)*/ 249 | this.minHorizontalEndRadian=0; 250 | /**最大水平结束弧度(单位:2D弧度、3D弧度)*/ 251 | this.maxHorizontalEndRadian=0; 252 | /**最小垂直结束弧度(单位:2D弧度、3D弧度)*/ 253 | this.minVerticalEndRadian=0; 254 | /**最大垂直结束弧度(单位:2D弧度、3D弧度)*/ 255 | this.maxVerticalEndRadian=0; 256 | /**false代表RGBA整体插值,true代表RGBA逐分量插值*/ 257 | this.colorComponentInter=false; 258 | /**false代表使用参数颜色数据,true代表使用原图颜色数据*/ 259 | this.disableColor=false; 260 | /**混合模式,待调整,引擎中暂无BlendState抽象*/ 261 | this.blendState=0; 262 | /**发射器类型,"point","box","sphere","ring"*/ 263 | this.emitterType="null"; 264 | /**发射器发射速率*/ 265 | this.emissionRate=0; 266 | /**球发射器半径*/ 267 | this.sphereEmitterRadius=1; 268 | /**球发射器速度*/ 269 | this.sphereEmitterVelocity=0; 270 | /**球发射器速度随机值*/ 271 | this.sphereEmitterVelocityAddVariance=0; 272 | /**环发射器半径*/ 273 | this.ringEmitterRadius=30; 274 | /**环发射器速度*/ 275 | this.ringEmitterVelocity=0; 276 | /**环发射器速度随机值*/ 277 | this.ringEmitterVelocityAddVariance=0; 278 | /**环发射器up向量,0代表X轴,1代表Y轴,2代表Z轴*/ 279 | this.ringEmitterUp=2; 280 | this.gravity=new Float32Array([0,0,0]); 281 | this.minStartColor=new Float32Array([1,1,1,1]); 282 | this.maxStartColor=new Float32Array([1,1,1,1]); 283 | this.minEndColor=new Float32Array([1,1,1,1]); 284 | this.maxEndColor=new Float32Array([1,1,1,1]); 285 | this.pointEmitterPosition=new Float32Array([0,0,0]); 286 | this.pointEmitterPositionVariance=new Float32Array([0,0,0]); 287 | this.pointEmitterVelocity=new Float32Array([0,0,0]); 288 | this.pointEmitterVelocityAddVariance=new Float32Array([0,0,0]); 289 | this.boxEmitterCenterPosition=new Float32Array([0,0,0]); 290 | this.boxEmitterSize=new Float32Array([0,0,0]); 291 | this.boxEmitterVelocity=new Float32Array([0,0,0]); 292 | this.boxEmitterVelocityAddVariance=new Float32Array([0,0,0]); 293 | this.sphereEmitterCenterPosition=new Float32Array([0,0,0]); 294 | this.ringEmitterCenterPosition=new Float32Array([0,0,0]); 295 | this.positionVariance=new Float32Array([0,0,0]); 296 | } 297 | 298 | __class(ParticleSetting,'laya.particle.ParticleSetting'); 299 | ParticleSetting.checkSetting=function(setting){ 300 | var key; 301 | for (key in ParticleSetting._defaultSetting){ 302 | if (!setting.hasOwnProperty(key)){ 303 | setting[key]=ParticleSetting._defaultSetting[key]; 304 | } 305 | } 306 | setting.endVelocity=+setting.endVelocity; 307 | setting.gravity[0]=+setting.gravity[0]; 308 | setting.gravity[1]=+setting.gravity[1]; 309 | setting.gravity[2]=+setting.gravity[2]; 310 | } 311 | 312 | __static(ParticleSetting, 313 | ['_defaultSetting',function(){return this._defaultSetting=new ParticleSetting();} 314 | ]); 315 | return ParticleSetting; 316 | })() 317 | 318 | 319 | /** 320 | *@private 321 | */ 322 | //class laya.particle.ParticleData 323 | var ParticleData=(function(){ 324 | function ParticleData(){ 325 | this.position=null; 326 | this.velocity=null; 327 | this.startColor=null; 328 | this.endColor=null; 329 | this.sizeRotation=null; 330 | this.radius=null; 331 | this.radian=null; 332 | this.durationAddScale=NaN; 333 | this.time=NaN; 334 | } 335 | 336 | __class(ParticleData,'laya.particle.ParticleData'); 337 | ParticleData.Create=function(settings,position,velocity,time){ 338 | var particleData=new ParticleData(); 339 | particleData.position=position; 340 | MathUtil.scaleVector3(velocity,settings.emitterVelocitySensitivity,ParticleData._tempVelocity); 341 | var horizontalVelocity=MathUtil.lerp(settings.minHorizontalVelocity,settings.maxHorizontalVelocity,Math.random()); 342 | var horizontalAngle=Math.random()*Math.PI *2; 343 | ParticleData._tempVelocity[0]+=horizontalVelocity *Math.cos(horizontalAngle); 344 | ParticleData._tempVelocity[2]+=horizontalVelocity *Math.sin(horizontalAngle); 345 | ParticleData._tempVelocity[1]+=MathUtil.lerp(settings.minVerticalVelocity,settings.maxVerticalVelocity,Math.random()); 346 | particleData.velocity=ParticleData._tempVelocity; 347 | particleData.startColor=ParticleData._tempStartColor; 348 | particleData.endColor=ParticleData._tempEndColor; 349 | var i=0; 350 | if (settings.disableColor){ 351 | for (i=0;i < 3;i++){ 352 | particleData.startColor[i]=1; 353 | particleData.endColor[i]=1; 354 | } 355 | particleData.startColor[i]=MathUtil.lerp(settings.minStartColor[i],settings.maxStartColor[i],Math.random()); 356 | particleData.endColor[i]=MathUtil.lerp(settings.minEndColor[i],settings.maxEndColor[i],Math.random()); 357 | } 358 | else{ 359 | if (settings.colorComponentInter){ 360 | for (i=0;i < 4;i++){ 361 | particleData.startColor[i]=MathUtil.lerp(settings.minStartColor[i],settings.maxStartColor[i],Math.random()); 362 | particleData.endColor[i]=MathUtil.lerp(settings.minEndColor[i],settings.maxEndColor[i],Math.random()); 363 | } 364 | }else { 365 | MathUtil.lerpVector4(settings.minStartColor,settings.maxStartColor,Math.random(),particleData.startColor); 366 | MathUtil.lerpVector4(settings.minEndColor,settings.maxEndColor,Math.random(),particleData.endColor); 367 | } 368 | } 369 | particleData.sizeRotation=ParticleData._tempSizeRotation; 370 | var sizeRandom=Math.random(); 371 | particleData.sizeRotation[0]=MathUtil.lerp(settings.minStartSize,settings.maxStartSize,sizeRandom); 372 | particleData.sizeRotation[1]=MathUtil.lerp(settings.minEndSize,settings.maxEndSize,sizeRandom); 373 | particleData.sizeRotation[2]=MathUtil.lerp(settings.minRotateSpeed,settings.maxRotateSpeed,Math.random()); 374 | particleData.radius=ParticleData._tempRadius; 375 | var radiusRandom=Math.random(); 376 | particleData.radius[0]=MathUtil.lerp(settings.minStartRadius,settings.maxStartRadius,radiusRandom); 377 | particleData.radius[1]=MathUtil.lerp(settings.minEndRadius,settings.maxEndRadius,radiusRandom); 378 | particleData.radian=ParticleData._tempRadian; 379 | particleData.radian[0]=MathUtil.lerp(settings.minHorizontalStartRadian,settings.maxHorizontalStartRadian,Math.random()); 380 | particleData.radian[1]=MathUtil.lerp(settings.minVerticalStartRadian,settings.maxVerticalStartRadian,Math.random()); 381 | var useEndRadian=settings.useEndRadian; 382 | particleData.radian[2]=useEndRadian?MathUtil.lerp(settings.minHorizontalEndRadian,settings.maxHorizontalEndRadian,Math.random()):particleData.radian[0]; 383 | particleData.radian[3]=useEndRadian?MathUtil.lerp(settings.minVerticalEndRadian,settings.maxVerticalEndRadian,Math.random()):particleData.radian[1]; 384 | particleData.durationAddScale=settings.ageAddScale *Math.random(); 385 | particleData.time=time; 386 | return particleData; 387 | } 388 | 389 | __static(ParticleData, 390 | ['_tempVelocity',function(){return this._tempVelocity=new Float32Array(3);},'_tempStartColor',function(){return this._tempStartColor=new Float32Array(4);},'_tempEndColor',function(){return this._tempEndColor=new Float32Array(4);},'_tempSizeRotation',function(){return this._tempSizeRotation=new Float32Array(3);},'_tempRadius',function(){return this._tempRadius=new Float32Array(2);},'_tempRadian',function(){return this._tempRadian=new Float32Array(4);} 391 | ]); 392 | return ParticleData; 393 | })() 394 | 395 | 396 | /** 397 | *@private 398 | */ 399 | //class laya.particle.shader.value.ParticleShaderValue extends laya.webgl.shader.d2.value.Value2D 400 | var ParticleShaderValue=(function(_super){ 401 | function ParticleShaderValue(){ 402 | /* 403 | public var a_CornerTextureCoordinate:Array=[4,WebGLContext.FLOAT,false,116,0]; 404 | public var a_Position:Array=[3,WebGLContext.FLOAT,false,116,16]; 405 | public var a_Velocity:Array=[3,WebGLContext.FLOAT,false,116,28]; 406 | public var a_StartColor:Array=[4,WebGLContext.FLOAT,false,116,40]; 407 | public var a_EndColor:Array=[4,WebGLContext.FLOAT,false,116,56]; 408 | public var a_SizeRotation:Array=[3,WebGLContext.FLOAT,false,116,72]; 409 | public var a_Radius:Array=[2,WebGLContext.FLOAT,false,116,84]; 410 | public var a_Radian:Array=[4,WebGLContext.FLOAT,false,116,92]; 411 | public var a_AgeAddScale:Array=[1,WebGLContext.FLOAT,false,116,108]; 412 | public var a_Time:Array=[1,WebGLContext.FLOAT,false,116,112]; 413 | */ 414 | this.u_CurrentTime=NaN; 415 | this.u_Duration=NaN; 416 | this.u_Gravity=null; 417 | //v3 418 | this.u_EndVelocity=NaN; 419 | this.u_texture=null; 420 | ParticleShaderValue.__super.call(this,0,0); 421 | } 422 | 423 | __class(ParticleShaderValue,'laya.particle.shader.value.ParticleShaderValue',_super); 424 | var __proto=ParticleShaderValue.prototype; 425 | /*�ŵ� ParticleShader ���� 426 | this._attribLocation=['a_CornerTextureCoordinate',0,'a_Position',1,'a_Velocity',2,'a_StartColor',3, 427 | 'a_EndColor',4,'a_SizeRotation',5,'a_Radius',6,'a_Radian',7,'a_AgeAddScale',8,'a_Time',9]; 428 | */ 429 | __proto.upload=function(){ 430 | var size=this.size; 431 | size[0]=RenderState2D.width; 432 | size[1]=RenderState2D.height; 433 | this.alpha=this.ALPHA *RenderState2D.worldAlpha; 434 | ParticleShaderValue.pShader.upload(this); 435 | } 436 | 437 | __static(ParticleShaderValue, 438 | ['pShader',function(){return this.pShader=new ParticleShader();} 439 | ]); 440 | return ParticleShaderValue; 441 | })(Value2D) 442 | 443 | 444 | /** 445 | *@private 446 | */ 447 | //class laya.particle.ParticleTemplateWebGL extends laya.particle.ParticleTemplateBase 448 | var ParticleTemplateWebGL=(function(_super){ 449 | function ParticleTemplateWebGL(parSetting){ 450 | this._vertices=null; 451 | //protected var _indexBuffer:Buffer; 452 | this._mesh=null; 453 | this._conchMesh=null; 454 | this._floatCountPerVertex=29; 455 | //0~3为CornerTextureCoordinate,4~6为Position,7~9Velocity,10到13为StartColor,14到17为EndColor,18到20位SizeRotation,21到22位Radius,23到26位Radian,27为DurationAddScaleShaderValue,28为Time 456 | this._firstActiveElement=0; 457 | this._firstNewElement=0; 458 | this._firstFreeElement=0; 459 | this._firstRetiredElement=0; 460 | this._currentTime=0; 461 | this._drawCounter=0; 462 | ParticleTemplateWebGL.__super.call(this); 463 | this.settings=parSetting; 464 | } 465 | 466 | __class(ParticleTemplateWebGL,'laya.particle.ParticleTemplateWebGL',_super); 467 | var __proto=ParticleTemplateWebGL.prototype; 468 | __proto.reUse=function(context,pos){ 469 | return 0; 470 | } 471 | 472 | __proto.initialize=function(){ 473 | var floatStride=0; 474 | this._vertices=this._mesh._vb.getFloat32Array(); 475 | floatStride=this._mesh._stride / 4; 476 | var bufi=0; 477 | var bufStart=0; 478 | for (var i=0;i < this.settings.maxPartices;i++){ 479 | var random=Math.random(); 480 | var cornerYSegement=this.settings.textureCount ? 1.0 / this.settings.textureCount :1.0; 481 | var cornerY=NaN; 482 | for (cornerY=0;cornerY < this.settings.textureCount;cornerY+=cornerYSegement){ 483 | if (random < cornerY+cornerYSegement) 484 | break ; 485 | } 486 | this._vertices[bufi++]=-1; 487 | this._vertices[bufi++]=-1; 488 | this._vertices[bufi++]=0; 489 | this._vertices[bufi++]=cornerY; 490 | bufi=(bufStart+=floatStride); 491 | this._vertices[bufi++]=1; 492 | this._vertices[bufi++]=-1; 493 | this._vertices[bufi++]=1; 494 | this._vertices[bufi++]=cornerY; 495 | bufi=bufStart+=floatStride; 496 | this._vertices[bufi++]=1; 497 | this._vertices[bufi++]=1; 498 | this._vertices[bufi++]=1; 499 | this._vertices[bufi++]=cornerY+cornerYSegement; 500 | bufi=bufStart+=floatStride; 501 | this._vertices[bufi++]=-1; 502 | this._vertices[bufi++]=1; 503 | this._vertices[bufi++]=0; 504 | this._vertices[bufi++]=cornerY+cornerYSegement; 505 | bufi=bufStart+=floatStride; 506 | } 507 | } 508 | 509 | __proto.update=function(elapsedTime){ 510 | this._currentTime+=elapsedTime / 1000; 511 | this.retireActiveParticles(); 512 | this.freeRetiredParticles(); 513 | if (this._firstActiveElement==this._firstFreeElement) 514 | this._currentTime=0; 515 | if (this._firstRetiredElement==this._firstActiveElement) 516 | this._drawCounter=0; 517 | } 518 | 519 | __proto.retireActiveParticles=function(){ 520 | var epsilon=0.0001; 521 | var particleDuration=this.settings.duration; 522 | while (this._firstActiveElement !=this._firstNewElement){ 523 | var offset=this._firstActiveElement *this._floatCountPerVertex *4; 524 | var index=offset+28; 525 | var particleAge=this._currentTime-this._vertices[index]; 526 | particleAge *=(1.0+this._vertices[offset+27]); 527 | if (particleAge+epsilon < particleDuration) 528 | break ; 529 | this._vertices[index]=this._drawCounter; 530 | this._firstActiveElement++; 531 | if (this._firstActiveElement >=this.settings.maxPartices) 532 | this._firstActiveElement=0; 533 | } 534 | } 535 | 536 | __proto.freeRetiredParticles=function(){ 537 | while (this._firstRetiredElement !=this._firstActiveElement){ 538 | var age=this._drawCounter-this._vertices[this._firstRetiredElement *this._floatCountPerVertex *4+28]; 539 | if (age < 3) 540 | break ; 541 | this._firstRetiredElement++; 542 | if (this._firstRetiredElement >=this.settings.maxPartices) 543 | this._firstRetiredElement=0; 544 | } 545 | } 546 | 547 | __proto.addNewParticlesToVertexBuffer=function(){} 548 | //由于循环队列判断算法,当下一个freeParticle等于retiredParticle时不添加例子,意味循环队列中永远有一个空位。(由于此判断算法快速、简单,所以放弃了使循环队列饱和的复杂算法(需判断freeParticle在retiredParticle前、后两种情况并不同处理)) 549 | __proto.addParticleArray=function(position,velocity){ 550 | var nextFreeParticle=this._firstFreeElement+1; 551 | if (nextFreeParticle >=this.settings.maxPartices) 552 | nextFreeParticle=0; 553 | if (nextFreeParticle===this._firstRetiredElement) 554 | return; 555 | var particleData=ParticleData.Create(this.settings,position,velocity,this._currentTime); 556 | var startIndex=this._firstFreeElement *this._floatCountPerVertex *4; 557 | for (var i=0;i < 4;i++){ 558 | var j=0,offset=0; 559 | for (j=0,offset=4;j < 3;j++) 560 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.position[j]; 561 | for (j=0,offset=7;j < 3;j++) 562 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.velocity[j]; 563 | for (j=0,offset=10;j < 4;j++) 564 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.startColor[j]; 565 | for (j=0,offset=14;j < 4;j++) 566 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.endColor[j]; 567 | for (j=0,offset=18;j < 3;j++) 568 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.sizeRotation[j]; 569 | for (j=0,offset=21;j < 2;j++) 570 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.radius[j]; 571 | for (j=0,offset=23;j < 4;j++) 572 | this._vertices[startIndex+i *this._floatCountPerVertex+offset+j]=particleData.radian[j]; 573 | this._vertices[startIndex+i *this._floatCountPerVertex+27]=particleData.durationAddScale; 574 | this._vertices[startIndex+i *this._floatCountPerVertex+28]=particleData.time; 575 | } 576 | this._firstFreeElement=nextFreeParticle; 577 | } 578 | 579 | return ParticleTemplateWebGL; 580 | })(ParticleTemplateBase) 581 | 582 | 583 | /** 584 | * 585 | *@private 586 | */ 587 | //class laya.particle.emitter.Emitter2D extends laya.particle.emitter.EmitterBase 588 | var Emitter2D=(function(_super){ 589 | function Emitter2D(_template){ 590 | this.setting=null; 591 | this._posRange=null; 592 | this._canvasTemplate=null; 593 | this._emitFun=null; 594 | Emitter2D.__super.call(this); 595 | this.template=_template; 596 | } 597 | 598 | __class(Emitter2D,'laya.particle.emitter.Emitter2D',_super); 599 | var __proto=Emitter2D.prototype; 600 | __proto.emit=function(){ 601 | _super.prototype.emit.call(this); 602 | if(this._emitFun!=null) 603 | this._emitFun(); 604 | } 605 | 606 | __proto.getRandom=function(value){ 607 | return (Math.random()*2-1)*value; 608 | } 609 | 610 | __proto.webGLEmit=function(){ 611 | var pos=new Float32Array(3); 612 | pos[0]=this.getRandom(this._posRange[0]); 613 | pos[1]=this.getRandom(this._posRange[1]); 614 | pos[2]=this.getRandom(this._posRange[2]); 615 | var v=new Float32Array(3); 616 | v[0]=0; 617 | v[1]=0; 618 | v[2]=0; 619 | this._particleTemplate.addParticleArray(pos,v); 620 | } 621 | 622 | __proto.canvasEmit=function(){ 623 | var pos=new Float32Array(3); 624 | pos[0]=this.getRandom(this._posRange[0]); 625 | pos[1]=this.getRandom(this._posRange[1]); 626 | pos[2]=this.getRandom(this._posRange[2]); 627 | var v=new Float32Array(3); 628 | v[0]=0; 629 | v[1]=0; 630 | v[2]=0; 631 | this._particleTemplate.addParticleArray(pos,v); 632 | } 633 | 634 | __getset(0,__proto,'template',function(){ 635 | return this._particleTemplate; 636 | },function(template){ 637 | this._particleTemplate=template; 638 | if (!template){ 639 | this._emitFun=null; 640 | this.setting=null; 641 | this._posRange=null; 642 | }; 643 | this.setting=template.settings; 644 | this._posRange=this.setting.positionVariance; 645 | if((this._particleTemplate instanceof laya.particle.ParticleTemplate2D )){ 646 | this._emitFun=this.webGLEmit; 647 | } 648 | }); 649 | 650 | return Emitter2D; 651 | })(EmitterBase) 652 | 653 | 654 | /** 655 | *@private 656 | */ 657 | //class laya.particle.ParticleTemplate2D extends laya.particle.ParticleTemplateWebGL 658 | var ParticleTemplate2D=(function(_super){ 659 | function ParticleTemplate2D(parSetting){ 660 | this.x=0; 661 | this.y=0; 662 | this._blendFn=null; 663 | this._startTime=0; 664 | this._key={}; 665 | this.sv=new ParticleShaderValue(); 666 | ParticleTemplate2D.__super.call(this,parSetting); 667 | var _this=this; 668 | Laya.loader.load(this.settings.textureName,Handler.create(null,function(texture){ 669 | _this.texture=texture; 670 | })); 671 | this.sv.u_Duration=this.settings.duration; 672 | this.sv.u_Gravity=this.settings.gravity; 673 | this.sv.u_EndVelocity=this.settings.endVelocity; 674 | this._blendFn=BlendMode.fns[parSetting.blendState]; 675 | this._mesh=MeshParticle2D.getAMesh(this.settings.maxPartices); 676 | this.initialize(); 677 | } 678 | 679 | __class(ParticleTemplate2D,'laya.particle.ParticleTemplate2D',_super); 680 | var __proto=ParticleTemplate2D.prototype; 681 | Laya.imps(__proto,{"laya.webgl.submit.ISubmit":true}) 682 | //loadContent(); 683 | __proto.getRenderType=function(){return-111} 684 | __proto.releaseRender=function(){} 685 | __proto.addParticleArray=function(position,velocity){ 686 | position[0]+=this.x; 687 | position[1]+=this.y; 688 | _super.prototype.addParticleArray.call(this,position,velocity); 689 | } 690 | 691 | /* 692 | override protected function loadContent():void{ 693 | var indexes:Uint16Array=new Uint16Array(settings.maxPartices *6); 694 | for (var i:int=0;i < settings.maxPartices;i++){ 695 | indexes[i *6+0]=(i *4+0); 696 | indexes[i *6+1]=(i *4+1); 697 | indexes[i *6+2]=(i *4+2); 698 | indexes[i *6+3]=(i *4+0); 699 | indexes[i *6+4]=(i *4+2); 700 | indexes[i *6+5]=(i *4+3); 701 | } 702 | _indexBuffer2D.clear(); 703 | _indexBuffer2D.append(indexes); 704 | _indexBuffer2D.upload(); 705 | } 706 | 707 | */ 708 | __proto.addNewParticlesToVertexBuffer=function(){ 709 | var _vertexBuffer2D=this._mesh._vb; 710 | _vertexBuffer2D.clear(); 711 | _vertexBuffer2D.append(this._vertices); 712 | var start=0; 713 | if (this._firstNewElement < this._firstFreeElement){ 714 | start=this._firstNewElement *4 *this._floatCountPerVertex *4; 715 | _vertexBuffer2D.subUpload(start,start,start+(this._firstFreeElement-this._firstNewElement)*4 *this._floatCountPerVertex *4); 716 | }else { 717 | start=this._firstNewElement *4 *this._floatCountPerVertex *4; 718 | _vertexBuffer2D.subUpload(start,start,start+(this.settings.maxPartices-this._firstNewElement)*4 *this._floatCountPerVertex *4); 719 | if (this._firstFreeElement > 0){ 720 | _vertexBuffer2D.setNeedUpload(); 721 | _vertexBuffer2D.subUpload(0,0,this._firstFreeElement *4 *this._floatCountPerVertex *4); 722 | } 723 | } 724 | this._firstNewElement=this._firstFreeElement; 725 | } 726 | 727 | __proto.renderSubmit=function(){ 728 | if (this.texture&&this.texture.getIsReady()){ 729 | this.update(Laya.timer._delta); 730 | this.sv.u_CurrentTime=this._currentTime; 731 | if (this._firstNewElement !=this._firstFreeElement){ 732 | this.addNewParticlesToVertexBuffer(); 733 | } 734 | this.blend(); 735 | if (this._firstActiveElement !=this._firstFreeElement){ 736 | var gl=WebGL.mainContext; 737 | this._mesh.useMesh(gl); 738 | this.sv.u_texture=this.texture._getSource(); 739 | this.sv.upload(); 740 | if (this._firstActiveElement < this._firstFreeElement){ 741 | WebGL.mainContext.drawElements(/*laya.webgl.WebGLContext.TRIANGLES*/0x0004,(this._firstFreeElement-this._firstActiveElement)*6,/*laya.webgl.WebGLContext.UNSIGNED_SHORT*/0x1403,this._firstActiveElement *6 *2); 742 | } 743 | else{ 744 | WebGL.mainContext.drawElements(/*laya.webgl.WebGLContext.TRIANGLES*/0x0004,(this.settings.maxPartices-this._firstActiveElement)*6,/*laya.webgl.WebGLContext.UNSIGNED_SHORT*/0x1403,this._firstActiveElement *6 *2); 745 | if (this._firstFreeElement > 0) 746 | WebGL.mainContext.drawElements(/*laya.webgl.WebGLContext.TRIANGLES*/0x0004,this._firstFreeElement *6,/*laya.webgl.WebGLContext.UNSIGNED_SHORT*/0x1403,0); 747 | } 748 | Stat.renderBatches++; 749 | } 750 | this._drawCounter++; 751 | } 752 | return 1; 753 | } 754 | 755 | __proto.updateParticleForNative=function(){ 756 | if (this.texture&&this.texture.getIsReady()){ 757 | this.update(Laya.timer._delta); 758 | this.sv.u_CurrentTime=this._currentTime; 759 | if (this._firstNewElement !=this._firstFreeElement){ 760 | this._firstNewElement=this._firstFreeElement; 761 | } 762 | } 763 | } 764 | 765 | __proto.getMesh=function(){ 766 | return this._mesh; 767 | } 768 | 769 | __proto.getConchMesh=function(){ 770 | return this._conchMesh; 771 | } 772 | 773 | __proto.getFirstNewElement=function(){ 774 | return this._firstNewElement; 775 | } 776 | 777 | __proto.getFirstFreeElement=function(){ 778 | return this._firstFreeElement; 779 | } 780 | 781 | __proto.getFirstActiveElement=function(){ 782 | return this._firstActiveElement; 783 | } 784 | 785 | __proto.getFirstRetiredElement=function(){ 786 | return this._firstRetiredElement; 787 | } 788 | 789 | __proto.setFirstFreeElement=function(_value){ 790 | this._firstFreeElement=_value; 791 | } 792 | 793 | __proto.setFirstNewElement=function(_value){ 794 | this._firstNewElement=_value; 795 | } 796 | 797 | __proto.addDrawCounter=function(){ 798 | this._drawCounter++; 799 | } 800 | 801 | __proto.blend=function(){ 802 | if (BlendMode.activeBlendFunction!==this._blendFn){ 803 | var gl=WebGL.mainContext; 804 | gl.enable(/*laya.webgl.WebGLContext.BLEND*/0x0BE2); 805 | this._blendFn(gl); 806 | BlendMode.activeBlendFunction=this._blendFn; 807 | } 808 | } 809 | 810 | __proto.dispose=function(){ 811 | this._mesh.releaseMesh(); 812 | } 813 | 814 | ParticleTemplate2D.activeBlendType=-1; 815 | return ParticleTemplate2D; 816 | })(ParticleTemplateWebGL) 817 | 818 | 819 | /** 820 | *Particle2D 类是2D粒子播放类 821 | * 822 | */ 823 | //class laya.particle.Particle2D extends laya.display.Sprite 824 | var Particle2D=(function(_super){ 825 | function Particle2D(setting){ 826 | /**@private */ 827 | this._particleTemplate=null; 828 | /**@private */ 829 | this._emitter=null; 830 | /**是否自动播放*/ 831 | this.autoPlay=true; 832 | this.tempCmd=null; 833 | Particle2D.__super.call(this); 834 | this._matrix4=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]); 835 | this.customRenderEnable=true; 836 | if (setting)this.setParticleSetting(setting); 837 | } 838 | 839 | __class(Particle2D,'laya.particle.Particle2D',_super); 840 | var __proto=Particle2D.prototype; 841 | /** 842 | *加载粒子文件 843 | *@param url 粒子文件地址 844 | */ 845 | __proto.load=function(url){ 846 | Laya.loader.load(url,Handler.create(this,this.setParticleSetting),null,/*laya.net.Loader.JSON*/"json"); 847 | } 848 | 849 | /** 850 | *设置粒子配置数据 851 | *@param settings 粒子配置数据 852 | */ 853 | __proto.setParticleSetting=function(setting){ 854 | if (!setting)return this.stop(); 855 | ParticleSetting.checkSetting(setting); 856 | this.customRenderEnable=true; 857 | this._particleTemplate=new ParticleTemplate2D(setting); 858 | this.graphics._saveToCmd(null,DrawParticleCmd.create(this._particleTemplate)); 859 | if (!this._emitter){ 860 | this._emitter=new Emitter2D(this._particleTemplate); 861 | }else { 862 | (this._emitter).template=this._particleTemplate; 863 | } 864 | if (this.autoPlay){ 865 | this.emitter.start(); 866 | this.play(); 867 | } 868 | } 869 | 870 | /** 871 | *播放 872 | */ 873 | __proto.play=function(){ 874 | Laya.timer.frameLoop(1,this,this._loop); 875 | } 876 | 877 | /** 878 | *停止 879 | */ 880 | __proto.stop=function(){ 881 | Laya.timer.clear(this,this._loop); 882 | } 883 | 884 | /**@private */ 885 | __proto._loop=function(){ 886 | this.advanceTime(1 / 60); 887 | } 888 | 889 | /** 890 | *时钟前进 891 | *@param passedTime 时钟前进时间 892 | */ 893 | __proto.advanceTime=function(passedTime){ 894 | (passedTime===void 0)&& (passedTime=1); 895 | if (this._emitter){ 896 | this._emitter.advanceTime(passedTime); 897 | } 898 | } 899 | 900 | __proto.customRender=function(context,x,y){ 901 | this._matrix4[0]=context._curMat.a; 902 | this._matrix4[1]=context._curMat.b; 903 | this._matrix4[4]=context._curMat.c; 904 | this._matrix4[5]=context._curMat.d; 905 | this._matrix4[12]=context._curMat.tx; 906 | this._matrix4[13]=context._curMat.ty; 907 | var sv=(this._particleTemplate).sv; 908 | sv.u_mmat=this._matrix4; 909 | } 910 | 911 | __proto.destroy=function(destroyChild){ 912 | (destroyChild===void 0)&& (destroyChild=true); 913 | if ((this._particleTemplate instanceof laya.particle.ParticleTemplate2D )) 914 | (this._particleTemplate).dispose(); 915 | _super.prototype.destroy.call(this,destroyChild); 916 | } 917 | 918 | /** 919 | *设置 粒子文件地址 920 | *@param path 粒子文件地址 921 | */ 922 | __getset(0,__proto,'url',null,function(url){ 923 | this.load(url); 924 | }); 925 | 926 | /** 927 | *获取粒子发射器 928 | */ 929 | __getset(0,__proto,'emitter',function(){ 930 | return this._emitter; 931 | }); 932 | 933 | return Particle2D; 934 | })(Sprite) 935 | 936 | 937 | /** 938 | *@private 939 | */ 940 | //class laya.particle.shader.ParticleShader extends laya.webgl.shader.Shader 941 | var ParticleShader=(function(_super){ 942 | //TODO:coverage 943 | function ParticleShader(){ 944 | ParticleShader.__super.call(this,ParticleShader.vs,ParticleShader.ps,"ParticleShader",null,['a_CornerTextureCoordinate',0,'a_Position',1,'a_Velocity',2,'a_StartColor',3, 945 | 'a_EndColor',4,'a_SizeRotation',5,'a_Radius',6,'a_Radian',7,'a_AgeAddScale',8,'a_Time',9]); 946 | } 947 | 948 | __class(ParticleShader,'laya.particle.shader.ParticleShader',_super); 949 | __static(ParticleShader, 950 | ['vs',function(){return this.vs="attribute vec4 a_CornerTextureCoordinate;\nattribute vec3 a_Position;\nattribute vec3 a_Velocity;\nattribute vec4 a_StartColor;\nattribute vec4 a_EndColor;\nattribute vec3 a_SizeRotation;\nattribute vec2 a_Radius;\nattribute vec4 a_Radian;\nattribute float a_AgeAddScale;\nattribute float a_Time;\n\nvarying vec4 v_Color;\nvarying vec2 v_TextureCoordinate;\n\nuniform float u_CurrentTime;\nuniform float u_Duration;\nuniform float u_EndVelocity;\nuniform vec3 u_Gravity;\n\nuniform vec2 size;\nuniform mat4 u_mmat;\n\nvec4 ComputeParticlePosition(in vec3 position, in vec3 velocity,in float age,in float normalizedAge)\n{\n\n float startVelocity = length(velocity);//起始标量速度\n float endVelocity = startVelocity * u_EndVelocity;//结束标量速度\n\n float velocityIntegral = startVelocity * normalizedAge +(endVelocity - startVelocity) * normalizedAge *normalizedAge/2.0;//计算当前速度的标量(单位空间),vt=v0*t+(1/2)*a*(t^2)\n \n vec3 addPosition = normalize(velocity) * velocityIntegral * u_Duration;//计算受自身速度影响的位置,转换标量到矢量 \n addPosition += u_Gravity * age * normalizedAge;//计算受重力影响的位置\n \n float radius=mix(a_Radius.x, a_Radius.y, normalizedAge); //计算粒子受半径和角度影响(无需计算角度和半径时,可用宏定义优化屏蔽此计算)\n float radianHorizontal =mix(a_Radian.x,a_Radian.z,normalizedAge);\n float radianVertical =mix(a_Radian.y,a_Radian.w,normalizedAge);\n \n float r =cos(radianVertical)* radius;\n addPosition.y += sin(radianVertical) * radius;\n \n addPosition.x += cos(radianHorizontal) *r;\n addPosition.z += sin(radianHorizontal) *r;\n \n addPosition.y=-addPosition.y;//2D粒子位置更新需要取负,2D粒子坐标系Y轴正向朝上\n position+=addPosition;\n return vec4(position,1.0);\n}\n\nfloat ComputeParticleSize(in float startSize,in float endSize, in float normalizedAge)\n{ \n float size = mix(startSize, endSize, normalizedAge);\n return size;\n}\n\nmat2 ComputeParticleRotation(in float rot,in float age)\n{ \n float rotation =rot * age;\n //计算2x2旋转矩阵.\n float c = cos(rotation);\n float s = sin(rotation);\n return mat2(c, -s, s, c);\n}\n\nvec4 ComputeParticleColor(in vec4 startColor,in vec4 endColor,in float normalizedAge)\n{\n vec4 color=mix(startColor,endColor,normalizedAge);\n //硬编码设置,使粒子淡入很快,淡出很慢,6.7的缩放因子把置归一在0到1之间,可以谷歌x*(1-x)*(1-x)*6.7的制图表\n color.a *= normalizedAge * (1.0-normalizedAge) * (1.0-normalizedAge) * 6.7;\n \n return color;\n}\n\nvoid main()\n{\n float age = u_CurrentTime - a_Time;\n age *= 1.0 + a_AgeAddScale;\n float normalizedAge = clamp(age / u_Duration,0.0,1.0);\n gl_Position = ComputeParticlePosition(a_Position, a_Velocity, age, normalizedAge);//计算粒子位置\n float pSize = ComputeParticleSize(a_SizeRotation.x,a_SizeRotation.y, normalizedAge);\n mat2 rotation = ComputeParticleRotation(a_SizeRotation.z, age);\n \n mat4 mat=u_mmat;\n gl_Position=vec4((mat*gl_Position).xy,0.0,1.0);\n gl_Position.xy += (rotation*a_CornerTextureCoordinate.xy) * pSize*vec2(mat[0][0],mat[1][1]);\n gl_Position=vec4((gl_Position.x/size.x-0.5)*2.0,(0.5-gl_Position.y/size.y)*2.0,0.0,1.0);\n \n v_Color = ComputeParticleColor(a_StartColor,a_EndColor, normalizedAge);\n v_TextureCoordinate =a_CornerTextureCoordinate.zw;\n}\n\n";},'ps',function(){return this.ps="#ifdef FSHIGHPRECISION\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n\nvarying vec4 v_Color;\nvarying vec2 v_TextureCoordinate;\nuniform sampler2D u_texture;\n\nvoid main()\n{ \n gl_FragColor=texture2D(u_texture,v_TextureCoordinate)*v_Color;\n gl_FragColor.xyz *= v_Color.w;\n}";} 951 | ]); 952 | return ParticleShader; 953 | })(Shader) 954 | 955 | 956 | 957 | })(window,document,Laya); 958 | -------------------------------------------------------------------------------- /bin/libs/laya.physics3D.wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/bin/libs/laya.physics3D.wasm.wasm -------------------------------------------------------------------------------- /bin/libs/worker.js: -------------------------------------------------------------------------------- 1 | function testCanImageData(){try{cc=new ImageData(20,20),canUseImageData=!0}catch(t){}}function loadImage(t){PNG.load(t.url,pngLoaded)}function loadImage2(t){var e,a=t.url;e=new XMLHttpRequest,e.open("GET",a,!0),e.responseType="arraybuffer",e.onload=function(){var t,i,r=e.response||e.mozResponseArrayBuffer;if(t=new Uint8Array(r),self.createImageBitmap)return void doCreateImageBitmap(t,a);try{startTime=getTimeNow(),i=new PNG(t),i.url=a,i.startTime=startTime,i.decodeEndTime=getTimeNow(),i.decodeTime=i.decodeEndTime-startTime,pngLoaded(i)}catch(s){pngFail(a,"parse fail"+s.toString()+":ya")}},e.onerror=function(t){pngFail(a,"loadFail")},e.send(null)}function doCreateImageBitmap(t,e){try{var a=getTimeNow();t=new self.Blob([t],{type:"image/png"}),self.createImageBitmap(t).then(function(t){var i={};i.url=e,i.imageBitmap=t,i.dataType="imageBitmap",i.startTime=a,i.decodeTime=getTimeNow()-a,i.sendTime=getTimeNow(),console.log("Decode By createImageBitmap,"+i.decodeTime,e),i.type="Image",postMessage(i,[i.imageBitmap])})["catch"](function(t){showMsgToMain("cache:"+t),pngFail(e,"parse fail"+t+":ya")})}catch(i){pngFail(e,"parse fail"+i.toString()+":ya")}}function getTimeNow(){return(new Date).getTime()}function pngFail(t,e){var a={};a.url=t,a.imagedata=null,a.type="Image",a.msg=e,console.log(e),postMessage(a)}function showMsgToMain(t){var e={};e.type="Msg",e.msg=t,postMessage(e)}function pngLoaded(t){var e={};e.url=t.url,canUseImageData?(e.imagedata=t.getImageData(),e.dataType="imagedata"):(e.buffer=t.getImageDataBuffer(),e.dataType="buffer"),e.width=t.width,e.height=t.height,e.decodeTime=getTimeNow()-t.startTime,console.log("Decode By PNG.js,"+(getTimeNow()-t.startTime),t.url),e.type="Image",canUseImageData?postMessage(e,[e.imagedata.data.buffer]):postMessage(e,[e.buffer.buffer])}var DecodeStream=function(){function t(){this.pos=0,this.bufferLength=0,this.eof=!1,this.buffer=null}return t.prototype={ensureBuffer:function(t){var e=this.buffer,a=e?e.byteLength:0;if(a>t)return e;for(var i=512;t>i;)i<<=1;for(var r=new Uint8Array(i),s=0;a>s;++s)r[s]=e[s];return this.buffer=r},getByte:function(){for(var t=this.pos;this.bufferLength<=t;){if(this.eof)return null;this.readBlock()}return this.buffer[this.pos++]},getBytes:function(t){var e=this.pos;if(t){this.ensureBuffer(e+t);for(var a=e+t;!this.eof&&this.bufferLengthi&&(a=i)}else{for(;!this.eof;)this.readBlock();var a=this.bufferLength}return this.pos=a,this.buffer.subarray(e,a)},lookChar:function(){for(var t=this.pos;this.bufferLength<=t;){if(this.eof)return null;this.readBlock()}return String.fromCharCode(this.buffer[this.pos])},getChar:function(){for(var t=this.pos;this.bufferLength<=t;){if(this.eof)return null;this.readBlock()}return String.fromCharCode(this.buffer[this.pos++])},makeSubStream:function(t,e,a){for(var i=t+e;this.bufferLength<=i&&!this.eof;)this.readBlock();return new Stream(this.buffer,t,e,a)},skip:function(t){t||(t=1),this.pos+=t},reset:function(){this.pos=0}},t}(),FlateStream=function(){function t(t){throw new Error(t)}function e(e){var a=0,i=e[a++],r=e[a++];-1!=i&&-1!=r||t("Invalid header in flate stream"),8!=(15&i)&&t("Unknown compression method in flate stream"),((i<<8)+r)%31!=0&&t("Bad FCHECK in flate stream"),32&r&&t("FDICT bit set in flate stream"),this.bytes=e,this.bytesPos=a,this.codeSize=0,this.codeBuf=0,DecodeStream.call(this)}var a=new Uint32Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),i=new Uint32Array([3,4,5,6,7,8,9,10,65547,65549,65551,65553,131091,131095,131099,131103,196643,196651,196659,196667,262211,262227,262243,262259,327811,327843,327875,327907,258,258,258]),r=new Uint32Array([1,2,3,4,65541,65543,131081,131085,196625,196633,262177,262193,327745,327777,393345,393409,459009,459137,524801,525057,590849,591361,657409,658433,724993,727041,794625,798721,868353,876545]),s=[new Uint32Array([459008,524368,524304,524568,459024,524400,524336,590016,459016,524384,524320,589984,524288,524416,524352,590048,459012,524376,524312,589968,459028,524408,524344,590032,459020,524392,524328,59e4,524296,524424,524360,590064,459010,524372,524308,524572,459026,524404,524340,590024,459018,524388,524324,589992,524292,524420,524356,590056,459014,524380,524316,589976,459030,524412,524348,590040,459022,524396,524332,590008,524300,524428,524364,590072,459009,524370,524306,524570,459025,524402,524338,590020,459017,524386,524322,589988,524290,524418,524354,590052,459013,524378,524314,589972,459029,524410,524346,590036,459021,524394,524330,590004,524298,524426,524362,590068,459011,524374,524310,524574,459027,524406,524342,590028,459019,524390,524326,589996,524294,524422,524358,590060,459015,524382,524318,589980,459031,524414,524350,590044,459023,524398,524334,590012,524302,524430,524366,590076,459008,524369,524305,524569,459024,524401,524337,590018,459016,524385,524321,589986,524289,524417,524353,590050,459012,524377,524313,589970,459028,524409,524345,590034,459020,524393,524329,590002,524297,524425,524361,590066,459010,524373,524309,524573,459026,524405,524341,590026,459018,524389,524325,589994,524293,524421,524357,590058,459014,524381,524317,589978,459030,524413,524349,590042,459022,524397,524333,590010,524301,524429,524365,590074,459009,524371,524307,524571,459025,524403,524339,590022,459017,524387,524323,589990,524291,524419,524355,590054,459013,524379,524315,589974,459029,524411,524347,590038,459021,524395,524331,590006,524299,524427,524363,590070,459011,524375,524311,524575,459027,524407,524343,590030,459019,524391,524327,589998,524295,524423,524359,590062,459015,524383,524319,589982,459031,524415,524351,590046,459023,524399,524335,590014,524303,524431,524367,590078,459008,524368,524304,524568,459024,524400,524336,590017,459016,524384,524320,589985,524288,524416,524352,590049,459012,524376,524312,589969,459028,524408,524344,590033,459020,524392,524328,590001,524296,524424,524360,590065,459010,524372,524308,524572,459026,524404,524340,590025,459018,524388,524324,589993,524292,524420,524356,590057,459014,524380,524316,589977,459030,524412,524348,590041,459022,524396,524332,590009,524300,524428,524364,590073,459009,524370,524306,524570,459025,524402,524338,590021,459017,524386,524322,589989,524290,524418,524354,590053,459013,524378,524314,589973,459029,524410,524346,590037,459021,524394,524330,590005,524298,524426,524362,590069,459011,524374,524310,524574,459027,524406,524342,590029,459019,524390,524326,589997,524294,524422,524358,590061,459015,524382,524318,589981,459031,524414,524350,590045,459023,524398,524334,590013,524302,524430,524366,590077,459008,524369,524305,524569,459024,524401,524337,590019,459016,524385,524321,589987,524289,524417,524353,590051,459012,524377,524313,589971,459028,524409,524345,590035,459020,524393,524329,590003,524297,524425,524361,590067,459010,524373,524309,524573,459026,524405,524341,590027,459018,524389,524325,589995,524293,524421,524357,590059,459014,524381,524317,589979,459030,524413,524349,590043,459022,524397,524333,590011,524301,524429,524365,590075,459009,524371,524307,524571,459025,524403,524339,590023,459017,524387,524323,589991,524291,524419,524355,590055,459013,524379,524315,589975,459029,524411,524347,590039,459021,524395,524331,590007,524299,524427,524363,590071,459011,524375,524311,524575,459027,524407,524343,590031,459019,524391,524327,589999,524295,524423,524359,590063,459015,524383,524319,589983,459031,524415,524351,590047,459023,524399,524335,590015,524303,524431,524367,590079]),9],n=[new Uint32Array([327680,327696,327688,327704,327684,327700,327692,327708,327682,327698,327690,327706,327686,327702,327694,0,327681,327697,327689,327705,327685,327701,327693,327709,327683,327699,327691,327707,327687,327703,327695,0]),5];return e.prototype=Object.create(DecodeStream.prototype),e.prototype.getBits=function(e){for(var a,i=this.codeSize,r=this.codeBuf,s=this.bytes,n=this.bytesPos;e>i;)"undefined"==typeof(a=s[n++])&&t("Bad encoding in flate stream"),r|=a<>e,this.codeSize=i-=e,this.bytesPos=n,a},e.prototype.getCode=function(e){for(var a=e[0],i=e[1],r=this.codeSize,s=this.codeBuf,n=this.bytes,o=this.bytesPos;i>r;){var h;"undefined"==typeof(h=n[o++])&&t("Bad encoding in flate stream"),s|=h<>16,c=65535&f;return(0==r||d>r||0==d)&&t("Bad encoding in flate stream"),this.codeBuf=s>>d,this.codeSize=r-d,this.bytesPos=o,c},e.prototype.generateHuffmanTable=function(t){for(var e=t.length,a=0,i=0;e>i;++i)t[i]>a&&(a=t[i]);for(var r=1<=n;++n,o<<=1,h<<=1)for(var f=0;e>f;++f)if(t[f]==n){for(var d=0,c=o,i=0;n>i;++i)d=d<<1|1&c,c>>=1;for(var i=d;r>i;i+=h)s[i]=n<<16|f;++o}return[s,a]},e.prototype.readBlock=function(){function e(t,e,a,i,r){for(var s=t.getBits(a)+i;s-- >0;)e[I++]=r}var o=this.getBits(3);if(1&o&&(this.eof=!0),o>>=1,0==o){var h,f=this.bytes,d=this.bytesPos;"undefined"==typeof(h=f[d++])&&t("Bad block header in flate stream");var c=h;"undefined"==typeof(h=f[d++])&&t("Bad block header in flate stream"),c|=h<<8,"undefined"==typeof(h=f[d++])&&t("Bad block header in flate stream");var l=h;"undefined"==typeof(h=f[d++])&&t("Bad block header in flate stream"),l|=h<<8,l!=(65535&~c)&&t("Bad uncompressed block length in flate stream"),this.codeBuf=0,this.codeSize=0;var u=this.bufferLength,p=this.ensureBuffer(u+c),g=u+c;this.bufferLength=g;for(var m=u;g>m;++m){if("undefined"==typeof(h=f[d++])){this.eof=!0;break}p[m]=h}return void(this.bytesPos=d)}var y,v;if(1==o)y=s,v=n;else if(2==o){for(var b=this.getBits(5)+257,w=this.getBits(5)+1,B=this.getBits(4)+4,T=Array(a.length),I=0;B>I;)T[a[I++]]=this.getBits(3);for(var U=this.generateHuffmanTable(T),D=0,I=0,k=b+w,A=new Array(k);k>I;){var C=this.getCode(U);16==C?e(this,A,2,3,D):17==C?e(this,A,3,3,D=0):18==C?e(this,A,7,11,D=0):A[I++]=D=C}y=this.generateHuffmanTable(A.slice(0,b)),v=this.generateHuffmanTable(A.slice(b,k))}else t("Unknown block type in flate stream");for(var p=this.buffer,S=p?p.length:0,P=this.bufferLength;;){var M=this.getCode(y);if(256>M)P+1>=S&&(p=this.ensureBuffer(P+1),S=p.length),p[P++]=M;else{if(256==M)return void(this.bufferLength=P);M-=257,M=i[M];var L=M>>16;L>0&&(L=this.getBits(L));var D=(65535&M)+L;M=this.getCode(v),M=r[M],L=M>>16,L>0&&(L=this.getBits(L));var x=(65535&M)+L;P+D>=S&&(p=this.ensureBuffer(P+D),S=p.length);for(var N=0;D>N;++N,++P)p[P]=p[P-x]}}},e}();(function(){var t;t=function(){function t(t){var e,a,i,r,s,n,o,h,f,d,c,l,u,p;for(this.data=t,this.pos=8,this.palette=[],this.imgData=[],this.transparency={},this.animation=null,this.text={},s=null;;){switch(e=this.readUInt32(),f=function(){var t,e;for(e=[],n=t=0;4>t;n=++t)e.push(String.fromCharCode(this.data[this.pos++]));return e}.call(this).join("")){case"IHDR":if(this.width=this.readUInt32(),this.height=this.readUInt32(),this.bits=this.data[this.pos++],this.colorType=this.data[this.pos++],this.compressionMethod=this.data[this.pos++],this.filterMethod=this.data[this.pos++],this.interlaceMethod=this.data[this.pos++],0!=this.interlaceMethod)throw new Error("Invalid interlaceMethod: "+this.interlaceMethod);break;case"acTL":this.animation={numFrames:this.readUInt32(),numPlays:this.readUInt32()||1/0,frames:[]};break;case"PLTE":this.palette=this.read(e);break;case"fcTL":s&&this.animation.frames.push(s),this.pos+=4,s={width:this.readUInt32(),height:this.readUInt32(),xOffset:this.readUInt32(),yOffset:this.readUInt32()},r=this.readUInt16(),i=this.readUInt16()||100,s.delay=1e3*r/i,s.disposeOp=this.data[this.pos++],s.blendOp=this.data[this.pos++],s.data=[];break;case"IDAT":case"fdAT":for("fdAT"===f&&(this.pos+=4,e-=4),t=(null!=s?s.data:void 0)||this.imgData,n=l=0;e>=0?e>l:l>e;n=e>=0?++l:--l)t.push(this.data[this.pos++]);break;case"tRNS":switch(this.transparency={},this.colorType){case 3:if(this.transparency.indexed=this.read(e),d=255-this.transparency.indexed.length,d>0)for(n=u=0;d>=0?d>u:u>d;n=d>=0?++u:--u)this.transparency.indexed.push(255);break;case 0:this.transparency.grayscale=this.read(e)[0];break;case 2:this.transparency.rgb=this.read(e)}break;case"tEXt":c=this.read(e),o=c.indexOf(0),h=String.fromCharCode.apply(String,c.slice(0,o)),this.text[h]=String.fromCharCode.apply(String,c.slice(o+1));break;case"IEND":return s&&this.animation.frames.push(s),this.colors=function(){switch(this.colorType){case 0:case 3:case 4:return 1;case 2:case 6:return 3}}.call(this),this.hasAlphaChannel=4===(p=this.colorType)||6===p,a=this.colors+(this.hasAlphaChannel?1:0),this.pixelBitlength=this.bits*a,this.colorSpace=function(){switch(this.colors){case 1:return"DeviceGray";case 3:return"DeviceRGB"}}.call(this),void(this.imgData=new Uint8Array(this.imgData));default:this.pos+=e}if(this.pos+=4,this.pos>this.data.length)throw new Error("Incomplete or corrupt PNG file")}}var e,a,i,r,s,n;return t.load=function(e,a){var i;return"function"==typeof canvas&&(a=canvas),i=new XMLHttpRequest,i.open("GET",e,!0),i.responseType="arraybuffer",i.onload=function(){var r,s;return r=new Uint8Array(i.response||i.mozResponseArrayBuffer),s=new t(r),s.url=e,"function"==typeof a?a(s):void 0},i.send(null)},r=0,i=1,s=2,a=0,e=1,t.prototype.read=function(t){var e,a,i;for(i=[],e=a=0;t>=0?t>a:a>t;e=t>=0?++a:--a)i.push(this.data[this.pos++]);return i},t.prototype.readUInt32=function(){var t,e,a,i;return t=this.data[this.pos++]<<24,e=this.data[this.pos++]<<16,a=this.data[this.pos++]<<8,i=this.data[this.pos++],t|e|a|i},t.prototype.readUInt16=function(){var t,e;return t=this.data[this.pos++]<<8,e=this.data[this.pos++],t|e},t.prototype.decodePixels=function(t){var e,a,i,r,s,n,o,h,f,d,c,l,u,p,g,m,y,v,b,w,B,T,I;if(null==t&&(t=this.imgData),0===t.length)return new Uint8Array(0);for(t=new FlateStream(t),t=t.getBytes(),l=this.pixelBitlength/8,m=l*this.width,u=new Uint8Array(m*this.height),n=t.length,g=0,p=0,a=0;n>p;){switch(t[p++]){case 0:for(r=b=0;m>b;r=b+=1)u[a++]=t[p++];break;case 1:for(r=w=0;m>w;r=w+=1)e=t[p++],s=l>r?0:u[a-l],u[a++]=(e+s)%256;break;case 2:for(r=B=0;m>B;r=B+=1)e=t[p++],i=(r-r%l)/l,y=g&&u[(g-1)*m+i*l+r%l],u[a++]=(y+e)%256;break;case 3:for(r=T=0;m>T;r=T+=1)e=t[p++],i=(r-r%l)/l,s=l>r?0:u[a-l],y=g&&u[(g-1)*m+i*l+r%l],u[a++]=(e+Math.floor((s+y)/2))%256;break;case 4:for(r=I=0;m>I;r=I+=1)e=t[p++],i=(r-r%l)/l,s=l>r?0:u[a-l],0===g?y=v=0:(y=u[(g-1)*m+i*l+r%l],v=i&&u[(g-1)*m+(i-1)*l+r%l]),o=s+y-v,h=Math.abs(o-s),d=Math.abs(o-y),c=Math.abs(o-v),f=d>=h&&c>=h?s:c>=d?y:v,u[a++]=(e+f)%256;break;default:throw new Error("Invalid filter algorithm: "+t[p-1])}g++}return u},t.prototype.decodePalette=function(){var t,e,a,i,r,s,n,o,h,f;i=this.palette,n=this.transparency.indexed||[];var d;for(d=4*i.length/3,s=new Uint8Array(d),r=0,a=i.length,t=0,e=o=0,h=i.length;h>o;e=o+=3)s[r++]=i[e],s[r++]=i[e+1],s[r++]=i[e+2],s[r++]=null!=(f=n[t++])?f:255;return s},t.prototype.getImageData=function(){var t=new self.ImageData(this.width,this.height);return this.copyToImageData(t,this.decodePixels()),t},t.prototype.getImageDataBuffer=function(){var t;return t=self.Uint8ClampedArray?new self.Uint8ClampedArray(this.width*this.height*4):new self.Uint8Array(this.width*this.height*4),this.copyToImageData(t,this.decodePixels()),t},t.prototype.copyToImageData=function(t,e){var a,i,r,s,n,o,h,f,d,c,l;if(i=this.colors,d=null,a=this.hasAlphaChannel,this.palette.length&&(d=null!=(l=this._decodedPalette)?l:this._decodedPalette=this.decodePalette(),i=4,a=!0),r=t.data||t,f=r.length,n=d||e,s=o=0,1===i)for(;f>s;)h=d?4*e[s/4]:o,c=n[h++],r[s++]=c,r[s++]=c,r[s++]=c,r[s++]=a?n[h++]:255,o=h;else for(;f>s;)h=d?4*e[s/4]:o,r[s++]=n[h++],r[s++]=n[h++],r[s++]=n[h++],r[s++]=a?n[h++]:255,o=h},t.prototype.decode=function(){var t;return t=new Uint8Array(this.width*this.height*4),this.copyToImageData(t,this.decodePixels()),t},t.prototype.decodeFrames=function(t){var e,a,i,r,s,o,h,f;if(this.animation){for(h=this.animation.frames,f=[],a=s=0,o=h.length;o>s;a=++s)e=h[a],i=t.createImageData(e.width,e.height),r=this.decodePixels(new Uint8Array(e.data)),this.copyToImageData(i,r),e.imageData=i,f.push(e.image=n(i));return f}},t}(),this.PNG=t}).call(this),onmessage=function(t){var e=t.data;switch(e.type){case"load":loadImage2(e)}};var canUseImageData=!1;testCanImageData(); -------------------------------------------------------------------------------- /bin/mainView.json: -------------------------------------------------------------------------------- 1 | {"type":"Scene","props":{"width":720,"height":1280},"compId":2,"child":[{"type":"Sprite","props":{"y":285,"x":382,"texture":"tankke.png"},"compId":3}],"loadList":["tankke.png"],"loadList3D":[]} -------------------------------------------------------------------------------- /bin/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件。", 3 | "setting": { 4 | "urlCheck": false, 5 | "es6": false, 6 | "postcss": true, 7 | "minified": false, 8 | "newFeature": true 9 | }, 10 | "compileType": "game", 11 | "libVersion": "game", 12 | "appid": "wx9170ca1ff6f31088", 13 | "projectname": "layaWXTest", 14 | "condition": { 15 | "search": { 16 | "current": -1, 17 | "list": [] 18 | }, 19 | "conversation": { 20 | "current": -1, 21 | "list": [] 22 | }, 23 | "game": { 24 | "currentL": -1, 25 | "list": [] 26 | }, 27 | "miniprogram": { 28 | "current": -1, 29 | "list": [] 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /bin/project.swan.json: -------------------------------------------------------------------------------- 1 | {"appid":"15052700","libversion":"game","compileType":"game","setting":{"urlCheck":true},"libVersion":"1.13.29","condition":{"swan":{"current":-2,"list":[]}}} -------------------------------------------------------------------------------- /bin/res/atlas/.rec: -------------------------------------------------------------------------------- 1 | D . 2 | R A7ED3403 DP1.png 3 | R 66A5AC0A tankke.png 4 | R 3755D3DC yangan.png 5 | -------------------------------------------------------------------------------- /bin/tankke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/bin/tankke.png -------------------------------------------------------------------------------- /bin/ui.json: -------------------------------------------------------------------------------- 1 | {"joystickView":{"type":"Scene","props":{"width":250,"visible":false,"name":"joystick","height":250},"compId":2,"child":[{"type":"Image","props":{"y":125,"x":125,"width":250,"var":"joystickBg","skin":"DP1.png","pivotY":125,"pivotX":125,"name":"joystickBg","height":250},"compId":3},{"type":"Image","props":{"y":124,"x":124,"width":100,"var":"joystickPoint","skin":"yangan.png","pivotY":50,"pivotX":50,"name":"joystickPoint","height":100},"compId":6}],"loadList":["DP1.png","yangan.png"],"loadList3D":[]},"mainView":{"type":"Scene","props":{"width":720,"height":1280},"compId":2,"child":[{"type":"Sprite","props":{"y":285,"x":382,"texture":"tankke.png"},"compId":3},{"type":"Script","props":{"top":0,"right":0,"left":0,"bottom":0,"runtime":"laya.ui.Widget"},"compId":4}],"loadList":["tankke.png"],"loadList3D":[]}} -------------------------------------------------------------------------------- /bin/unpack.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /bin/version.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /bin/yangan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/bin/yangan.png -------------------------------------------------------------------------------- /joystick.laya: -------------------------------------------------------------------------------- 1 | {"proName":"joystick","engineType":0,"proType":1,"layaProType":0,"version":"2.0.0"} -------------------------------------------------------------------------------- /laya/.laya: -------------------------------------------------------------------------------- 1 | 2 | img,temp,sound 3 | embed 4 | png,jpg 5 | bin/res/atlas 6 | bin 7 | src/ui 8 | 9 | 11 | 1 12 | bin/ui.json 13 | Sprite,Box,List,Tab,RadioGroup,ViewStack,Panel,HBox,VBox,Tree 14 | Scene,View,Dialog 15 | 16 | 1 17 | 18 | 80 19 | 20 | 21 | 24 | 2048 25 | 2048 26 | 512 27 | 512 28 | false 29 | true 30 | mainView.scene 31 | 2D 32 | fixedwidth 33 | none 34 | top 35 | left 36 | 720 37 | 1280 38 | src/view 39 | 0 40 | 1 41 | 42 | false 43 | true 44 | true 45 | false 46 | 47 | 2.0 48 | false 49 | false 50 | true 51 | false 52 | 53 | -------------------------------------------------------------------------------- /laya/assets/DP1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/laya/assets/DP1.png -------------------------------------------------------------------------------- /laya/assets/tankke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/laya/assets/tankke.png -------------------------------------------------------------------------------- /laya/assets/yangan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizenghua/JoyStick/f6b04af496d90c5a15b5f17223ad85c2f9234418/laya/assets/yangan.png -------------------------------------------------------------------------------- /laya/ignore.cfg: -------------------------------------------------------------------------------- 1 | {"src/Main.ts":true,"src/GameConfig.ts":true,"src/ui":true} -------------------------------------------------------------------------------- /laya/pages/joystickView.scene: -------------------------------------------------------------------------------- 1 | { 2 | "x":0, 3 | "type":"Scene", 4 | "selectedBox":2, 5 | "selecteID":6, 6 | "searchKey":"Scene,joystick", 7 | "props":{"width":250,"visible":false,"sceneColor":"#000000","name":"joystick","height":250}, 8 | "nodeParent":-1, 9 | "maxID":8, 10 | "label":"joystick", 11 | "isOpen":true, 12 | "isDirectory":true, 13 | "isAniNode":true, 14 | "hasChild":true, 15 | "compId":2, 16 | "child":[ 17 | { 18 | "x":15, 19 | "type":"Image", 20 | "searchKey":"Image,joystickBg,joystickBg", 21 | "props":{"y":125,"x":125,"width":250,"var":"joystickBg","skin":"DP1.png","pivotY":125,"pivotX":125,"name":"joystickBg","height":250}, 22 | "nodeParent":2, 23 | "label":"joystickBg", 24 | "isDirectory":false, 25 | "isAniNode":true, 26 | "hasChild":false, 27 | "compId":3, 28 | "child":[ 29 | ] 30 | }, 31 | { 32 | "x":15, 33 | "type":"Image", 34 | "searchKey":"Image,joystickPoint,joystickPoint", 35 | "props":{"y":124,"x":124,"width":100,"var":"joystickPoint","skin":"yangan.png","pivotY":50,"pivotX":50,"name":"joystickPoint","height":100}, 36 | "nodeParent":2, 37 | "label":"joystickPoint", 38 | "isDirectory":false, 39 | "isAniNode":true, 40 | "hasChild":false, 41 | "compId":6, 42 | "child":[ 43 | ] 44 | }], 45 | "animations":[ 46 | { 47 | "nodes":[ 48 | ], 49 | "name":"ani1", 50 | "id":1, 51 | "frameRate":24, 52 | "action":0 53 | }] 54 | } -------------------------------------------------------------------------------- /laya/pages/mainView.scene: -------------------------------------------------------------------------------- 1 | { 2 | "x":0, 3 | "type":"Scene", 4 | "selectedBox":2, 5 | "selecteID":4, 6 | "searchKey":"Scene", 7 | "props":{"width":720,"sceneColor":"#000000","height":1280}, 8 | "nodeParent":-1, 9 | "maxID":5, 10 | "label":"Scene", 11 | "isOpen":true, 12 | "isDirectory":true, 13 | "isAniNode":true, 14 | "hasChild":true, 15 | "compId":2, 16 | "child":[ 17 | { 18 | "x":15, 19 | "type":"Sprite", 20 | "searchKey":"Sprite", 21 | "props":{"y":285,"x":382,"texture":"tankke.png"}, 22 | "nodeParent":2, 23 | "label":"Sprite", 24 | "isDirectory":false, 25 | "isAniNode":true, 26 | "hasChild":false, 27 | "compId":3, 28 | "child":[ 29 | ] 30 | }, 31 | { 32 | "x":15, 33 | "type":"Widget", 34 | "switchAble":true, 35 | "searchKey":"Widget", 36 | "removeAble":true, 37 | "props":{"top":0,"right":0,"left":0,"bottom":0}, 38 | "nodeParent":2, 39 | "label":"Widget", 40 | "isDirectory":false, 41 | "isAniNode":true, 42 | "hasChild":false, 43 | "compId":4, 44 | "child":[ 45 | ] 46 | }], 47 | "animations":[ 48 | { 49 | "nodes":[ 50 | ], 51 | "name":"ani1", 52 | "id":1, 53 | "frameRate":24, 54 | "action":0 55 | }] 56 | } -------------------------------------------------------------------------------- /libs/layaAir.minigame.d.ts: -------------------------------------------------------------------------------- 1 | declare module laya.wx.mini { 2 | import Handler = laya.utils.Handler; 3 | class MiniAdpter { 4 | static EnvConfig: any; 5 | /**全局window对象**/ 6 | static window: any; 7 | static systemInfo: any; 8 | static isZiYu: boolean; 9 | static isPosMsgYu: boolean; 10 | /**是否自动缓存下载的图片跟声音文件,默认为true**/ 11 | static autoCacheFile: boolean; 12 | /**50M缓存容量满时每次清理容量值,默认每次清理5M**/ 13 | static minClearSize: number; 14 | /**本地资源列表**/ 15 | static nativefiles: Array; 16 | /**本地分包资源表**/ 17 | static subNativeFiles: any; 18 | /**本地分包文件目录数组**/ 19 | static subNativeheads: Array; 20 | /**本地分包文件目录映射表**/ 21 | static subMaps: Array; 22 | static AutoCacheDownFile: boolean; 23 | static getJson(data: string): any; 24 | /**激活微信小游戏适配器*/ 25 | static enable(): void; 26 | /** 27 | * 初始化回调 28 | * @param isPosMsg 是否需要在主域中自动将加载的文本数据自动传递到子域,默认 false 29 | * @param isSon 是否是子域,默认为false 30 | */ 31 | static init(isPosMsg?: boolean, isSon?: boolean): void; 32 | /** 33 | * 获取url对应的encoding值 34 | * @param url 文件路径 35 | * @param type 文件类型 36 | * @return 37 | */ 38 | static getUrlEncode(url: string, type: string): string; 39 | /** 40 | * 下载文件 41 | * @param fileUrl 文件地址(全路径) 42 | * @param fileType 文件类型(image、text、json、xml、arraybuffer、sound、atlas、font) 43 | * @param callBack 文件加载回调,回调内容[errorCode码(0成功,1失败,2加载进度) 44 | * @param encoding 文件编码默认utf8,非图片文件加载需要设置相应的编码,二进制编码为空字符串 45 | */ 46 | static downLoadFile(fileUrl: string, fileType?: string, callBack?: Handler, encoding?: string): void; 47 | /** 48 | * 从本地删除文件 49 | * @param fileUrl 文件地址(全路径) 50 | * @param callBack 回调处理,在存储图片时用到 51 | */ 52 | static remove(fileUrl: string, callBack?: Handler): void; 53 | /** 54 | * 清空缓存空间文件内容 55 | */ 56 | static removeAll(): void; 57 | /** 58 | * 判断是否是4M包文件 59 | * @param fileUrl 文件地址(全路径) 60 | * @return 61 | */ 62 | static hasNativeFile(fileUrl: string): boolean; 63 | /** 64 | * 判断缓存里是否存在文件 65 | * @param fileUrl 文件地址(全路径) 66 | * @return 67 | */ 68 | static getFileInfo(fileUrl: string): any; 69 | /** 70 | * 获取缓存文件列表 71 | * @return 72 | */ 73 | static getFileList(): any; 74 | static exitMiniProgram(): void; 75 | static pixelRatio(): number; 76 | static createElement(type: string): any; 77 | static createShaderCondition(conditionScript: string): Function; 78 | /** 79 | * 传递图集url地址到 80 | * @param url 为绝对地址 81 | */ 82 | static sendAtlasToOpenDataContext(url: string): void; 83 | /** 84 | * 发送单张图片到开放数据域 85 | * @param url 86 | */ 87 | static sendSinglePicToOpenDataContext(url: string): void; 88 | /** 89 | * 传递json配置数据到开放数据域 90 | * @param url 为绝对地址 91 | */ 92 | static sendJsonDataToDataContext(url: string): void; 93 | } 94 | } 95 | declare module laya.bd.mini { 96 | import Handler = laya.utils.Handler; 97 | class BMiniAdapter { 98 | static EnvConfig: any; 99 | /**全局window对象**/ 100 | static window: any; 101 | static systemInfo: any; 102 | static isZiYu: boolean; 103 | static isPosMsgYu: boolean; 104 | /**是否自动缓存下载的图片跟声音文件,默认为true**/ 105 | static autoCacheFile: boolean; 106 | /**50M缓存容量满时每次清理容量值,默认每次清理5M**/ 107 | static minClearSize: number; 108 | /**本地资源列表**/ 109 | static nativefiles: Array; 110 | /**本地分包资源表**/ 111 | static subNativeFiles: any; 112 | /**本地分包文件目录数组**/ 113 | static subNativeheads: Array; 114 | /**本地分包文件目录映射表**/ 115 | static subMaps: Array; 116 | static AutoCacheDownFile: boolean; 117 | static getJson(data: string): any; 118 | /**激活微信小游戏适配器*/ 119 | static enable(): void; 120 | /** 121 | * 初始化回调 122 | * @param isPosMsg 是否需要在主域中自动将加载的文本数据自动传递到子域,默认 false 123 | * @param isSon 是否是子域,默认为false 124 | */ 125 | static init(isPosMsg?: boolean, isSon?: boolean): void; 126 | /** 127 | * 获取url对应的encoding值 128 | * @param url 文件路径 129 | * @param type 文件类型 130 | * @return 131 | */ 132 | static getUrlEncode(url: string, type: string): string; 133 | /** 134 | * 下载文件 135 | * @param fileUrl 文件地址(全路径) 136 | * @param fileType 文件类型(image、text、json、xml、arraybuffer、sound、atlas、font) 137 | * @param callBack 文件加载回调,回调内容[errorCode码(0成功,1失败,2加载进度) 138 | * @param encoding 文件编码默认utf8,非图片文件加载需要设置相应的编码,二进制编码为空字符串 139 | */ 140 | static downLoadFile(fileUrl: string, fileType?: string, callBack?: Handler, encoding?: string): void; 141 | /** 142 | * 从本地删除文件 143 | * @param fileUrl 文件地址(全路径) 144 | * @param callBack 回调处理,在存储图片时用到 145 | */ 146 | static remove(fileUrl: string, callBack?: Handler): void; 147 | /** 148 | * 清空缓存空间文件内容 149 | */ 150 | static removeAll(): void; 151 | /** 152 | * 判断是否是4M包文件 153 | * @param fileUrl 文件地址(全路径) 154 | * @return 155 | */ 156 | static hasNativeFile(fileUrl: string): boolean; 157 | /** 158 | * 判断缓存里是否存在文件 159 | * @param fileUrl 文件地址(全路径) 160 | * @return 161 | */ 162 | static getFileInfo(fileUrl: string): any; 163 | /** 164 | * 获取缓存文件列表 165 | * @return 166 | */ 167 | static getFileList(): any; 168 | static exitMiniProgram(): void; 169 | static pixelRatio(): number; 170 | static createElement(type: string): any; 171 | static createShaderCondition(conditionScript: string): Function; 172 | /** 173 | * 传递图集url地址到 174 | * @param url 为绝对地址 175 | */ 176 | static sendAtlasToOpenDataContext(url: string): void; 177 | /** 178 | * 发送单张图片到开放数据域 179 | * @param url 180 | */ 181 | static sendSinglePicToOpenDataContext(url: string): void; 182 | /** 183 | * 传递json配置数据到开放数据域 184 | * @param url 为绝对地址 185 | */ 186 | static sendJsonDataToDataContext(url: string): void; 187 | } 188 | } 189 | declare module laya.mi.mini { 190 | import Handler = laya.utils.Handler; 191 | class KGMiniAdapter { 192 | static EnvConfig: any; 193 | /**全局window对象**/ 194 | static window: any; 195 | static systemInfo: any; 196 | static isZiYu: boolean; 197 | static isPosMsgYu: boolean; 198 | /**是否自动缓存下载的图片跟声音文件,默认为true**/ 199 | static autoCacheFile: boolean; 200 | /**50M缓存容量满时每次清理容量值,默认每次清理5M**/ 201 | static minClearSize: number; 202 | /**本地资源列表**/ 203 | static nativefiles: Array; 204 | /**本地分包资源表**/ 205 | static subNativeFiles: any; 206 | /**本地分包文件目录数组**/ 207 | static subNativeheads: Array; 208 | /**本地分包文件目录映射表**/ 209 | static subMaps: Array; 210 | static AutoCacheDownFile: boolean; 211 | static getJson(data: string): any; 212 | /**激活微信小游戏适配器*/ 213 | static enable(): void; 214 | /** 215 | * 初始化回调 216 | * @param isPosMsg 是否需要在主域中自动将加载的文本数据自动传递到子域,默认 false 217 | * @param isSon 是否是子域,默认为false 218 | */ 219 | static init(isPosMsg?: boolean, isSon?: boolean): void; 220 | /** 221 | * 获取url对应的encoding值 222 | * @param url 文件路径 223 | * @param type 文件类型 224 | * @return 225 | */ 226 | static getUrlEncode(url: string, type: string): string; 227 | /** 228 | * 下载文件 229 | * @param fileUrl 文件地址(全路径) 230 | * @param fileType 文件类型(image、text、json、xml、arraybuffer、sound、atlas、font) 231 | * @param callBack 文件加载回调,回调内容[errorCode码(0成功,1失败,2加载进度) 232 | * @param encoding 文件编码默认utf8,非图片文件加载需要设置相应的编码,二进制编码为空字符串 233 | */ 234 | static downLoadFile(fileUrl: string, fileType?: string, callBack?: Handler, encoding?: string): void; 235 | /** 236 | * 从本地删除文件 237 | * @param fileUrl 文件地址(全路径) 238 | * @param callBack 回调处理,在存储图片时用到 239 | */ 240 | static remove(fileUrl: string, callBack?: Handler): void; 241 | /** 242 | * 清空缓存空间文件内容 243 | */ 244 | static removeAll(): void; 245 | /** 246 | * 判断是否是4M包文件 247 | * @param fileUrl 文件地址(全路径) 248 | * @return 249 | */ 250 | static hasNativeFile(fileUrl: string): boolean; 251 | /** 252 | * 判断缓存里是否存在文件 253 | * @param fileUrl 文件地址(全路径) 254 | * @return 255 | */ 256 | static getFileInfo(fileUrl: string): any; 257 | /** 258 | * 获取缓存文件列表 259 | * @return 260 | */ 261 | static getFileList(): any; 262 | static exitMiniProgram(): void; 263 | static pixelRatio(): number; 264 | static createElement(type: string): any; 265 | static createShaderCondition(conditionScript: string): Function; 266 | /** 267 | * 传递图集url地址到 268 | * @param url 为绝对地址 269 | */ 270 | static sendAtlasToOpenDataContext(url: string): void; 271 | /** 272 | * 发送单张图片到开放数据域 273 | * @param url 274 | */ 275 | static sendSinglePicToOpenDataContext(url: string): void; 276 | /** 277 | * 传递json配置数据到开放数据域 278 | * @param url 为绝对地址 279 | */ 280 | static sendJsonDataToDataContext(url: string): void; 281 | } 282 | } 283 | declare module laya.qg.mini { 284 | import Handler = laya.utils.Handler; 285 | class QGMiniAdapter { 286 | static EnvConfig: any; 287 | /**全局window对象**/ 288 | static window: any; 289 | static systemInfo: any; 290 | static isZiYu: boolean; 291 | static isPosMsgYu: boolean; 292 | /**是否自动缓存下载的图片跟声音文件,默认为true**/ 293 | static autoCacheFile: boolean; 294 | /**50M缓存容量满时每次清理容量值,默认每次清理5M**/ 295 | static minClearSize: number; 296 | /**本地资源列表**/ 297 | static nativefiles: Array; 298 | /**本地分包资源表**/ 299 | static subNativeFiles: any; 300 | /**本地分包文件目录数组**/ 301 | static subNativeheads: Array; 302 | /**本地分包文件目录映射表**/ 303 | static subMaps: Array; 304 | static AutoCacheDownFile: boolean; 305 | static getJson(data: string): any; 306 | /**激活微信小游戏适配器*/ 307 | static enable(): void; 308 | /** 309 | * 初始化回调 310 | * @param isPosMsg 是否需要在主域中自动将加载的文本数据自动传递到子域,默认 false 311 | * @param isSon 是否是子域,默认为false 312 | */ 313 | static init(isPosMsg?: boolean, isSon?: boolean): void; 314 | /** 315 | * 获取url对应的encoding值 316 | * @param url 文件路径 317 | * @param type 文件类型 318 | * @return 319 | */ 320 | static getUrlEncode(url: string, type: string): string; 321 | /** 322 | * 下载文件 323 | * @param fileUrl 文件地址(全路径) 324 | * @param fileType 文件类型(image、text、json、xml、arraybuffer、sound、atlas、font) 325 | * @param callBack 文件加载回调,回调内容[errorCode码(0成功,1失败,2加载进度) 326 | * @param encoding 文件编码默认utf8,非图片文件加载需要设置相应的编码,二进制编码为空字符串 327 | */ 328 | static downLoadFile(fileUrl: string, fileType?: string, callBack?: Handler, encoding?: string): void; 329 | /** 330 | * 从本地删除文件 331 | * @param fileUrl 文件地址(全路径) 332 | * @param callBack 回调处理,在存储图片时用到 333 | */ 334 | static remove(fileUrl: string, callBack?: Handler): void; 335 | /** 336 | * 清空缓存空间文件内容 337 | */ 338 | static removeAll(): void; 339 | /** 340 | * 判断是否是4M包文件 341 | * @param fileUrl 文件地址(全路径) 342 | * @return 343 | */ 344 | static hasNativeFile(fileUrl: string): boolean; 345 | /** 346 | * 判断缓存里是否存在文件 347 | * @param fileUrl 文件地址(全路径) 348 | * @return 349 | */ 350 | static getFileInfo(fileUrl: string): any; 351 | /** 352 | * 获取缓存文件列表 353 | * @return 354 | */ 355 | static getFileList(): any; 356 | static exitMiniProgram(): void; 357 | static pixelRatio(): number; 358 | static createElement(type: string): any; 359 | static createShaderCondition(conditionScript: string): Function; 360 | /** 361 | * 传递图集url地址到 362 | * @param url 为绝对地址 363 | */ 364 | static sendAtlasToOpenDataContext(url: string): void; 365 | /** 366 | * 发送单张图片到开放数据域 367 | * @param url 368 | */ 369 | static sendSinglePicToOpenDataContext(url: string): void; 370 | /** 371 | * 传递json配置数据到开放数据域 372 | * @param url 为绝对地址 373 | */ 374 | static sendJsonDataToDataContext(url: string): void; 375 | } 376 | } 377 | 378 | declare module Laya { 379 | class MiniAdpter extends laya.wx.mini.MiniAdpter { 380 | } 381 | class BMiniAdapter extends laya.bd.mini.BMiniAdapter { 382 | } 383 | class KGMiniAdapter extends laya.mi.mini.KGMiniAdapter { 384 | } 385 | class QGMiniAdapter extends laya.qg.mini.QGMiniAdapter { 386 | } 387 | } -------------------------------------------------------------------------------- /src/GameConfig.ts: -------------------------------------------------------------------------------- 1 | /**This class is automatically generated by LayaAirIDE, please do not make any modifications. */ 2 | 3 | /* 4 | * 游戏初始化配置; 5 | */ 6 | export default class GameConfig{ 7 | static width:number=720; 8 | static height:number=1280; 9 | static scaleMode:string="fixedwidth"; 10 | static screenMode:string="none"; 11 | static alignV:string="top"; 12 | static alignH:string="left"; 13 | static startScene:any="mainView.scene"; 14 | static sceneRoot:string=""; 15 | static debug:boolean=false; 16 | static stat:boolean=false; 17 | static physicsDebug:boolean=false; 18 | static exportSceneToJson:boolean=true; 19 | constructor(){} 20 | static init(){ 21 | var reg: Function = Laya.ClassUtils.regClass; 22 | 23 | } 24 | } 25 | GameConfig.init(); -------------------------------------------------------------------------------- /src/JoyStick.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @ Description: 摇杆控制类 3 | * @ Author: lzh 4 | * @ Date: 2019-07-17 16:31:55 5 | * @ Last Modified by: lzh 6 | * @ Last Modified time: 2019-07-18 17:33:26 7 | * @ copyright: youai 8 | */ 9 | 10 | import { ui } from "./ui/layaMaxUI"; 11 | import Event = Laya.Event; 12 | 13 | export class JoyStick extends ui.joystickViewUI { 14 | /**最大滑动距离(超过距离则显示操纵杆) */ 15 | private readonly _MaxMoveDistance: number = 10; 16 | /**触摸区域 */ 17 | private _touchRect: Laya.Sprite; 18 | /**控制器中心点X坐标 */ 19 | private _originPiontX: number; 20 | /**控制器中心点Y坐标 */ 21 | private _originPiontY: number; 22 | /**控制器中心点 */ 23 | private _originPiont: Laya.Point; 24 | /**操纵杆与控制中心点的距离(摇杆的滑动范围) */ 25 | private _joystickRadius: number; 26 | /**摇杆与中心点的x轴距离 */ 27 | private _deltaX: number; 28 | /**摇杆与中心点的y轴距离 */ 29 | private _deltaY: number; 30 | /** 开始点击时的舞台X坐标 */ 31 | private _startStageX: number; 32 | /** 开始点击时的舞台Y坐标 */ 33 | private _startStageY: number; 34 | /**当前多点触摸id 防止摇杆上出现第二个手指时干扰第一个手指*/ 35 | private _curTouchId: number = -1; 36 | /**是否触发TouchMove事件,触发则拦截Click事件 */ 37 | private _isTouchMove: Boolean = false; 38 | /**摇杆的角度 */ 39 | public angle: number = -1; 40 | /**摇杆的弧度 */ 41 | public radians: number = -1; 42 | 43 | 44 | constructor(touchSp: Laya.Sprite) { 45 | super(); 46 | this._touchRect = touchSp; 47 | 48 | this._touchRect.on(Event.MOUSE_DOWN, this, this._onMouseDown); 49 | this._touchRect.on(Event.MOUSE_UP, this, this._onMouseUp); 50 | this._touchRect.on(Event.MOUSE_OUT, this, this._onMouseUp); 51 | } 52 | 53 | public onAwake(): void { 54 | this._originPiontX = this.width / 2; 55 | this._originPiontY = this.height / 2; 56 | this._originPiont = new Laya.Point(this._originPiontX, this._originPiontY); 57 | this._joystickRadius = this._originPiontX - this.joystickPoint.width / 2; 58 | this.visible = false; 59 | } 60 | 61 | /** 62 | * 鼠标按下事件回调 63 | * @param evt 64 | */ 65 | private _onMouseDown(evt: Event): void { 66 | //记录当前按下id 67 | this._curTouchId = evt.touchId; 68 | // 记录点击的坐标点 69 | this._startStageX = evt.stageX; 70 | this._startStageY = evt.stageY; 71 | this._isTouchMove = false; 72 | //更新摇杆到屏幕按下位置 73 | this.pos(Laya.stage.mouseX - this._originPiontX, Laya.stage.mouseY - this._originPiontY); 74 | //初始化摇杆控制点位置 75 | this.joystickPoint.pos(this._originPiontX, this._originPiontY); 76 | this._touchRect.on(Event.MOUSE_MOVE, this, this._onMouseMove); 77 | } 78 | 79 | /** 80 | * 鼠标移动事件回调 81 | * @param evt 82 | */ 83 | private _onMouseMove(evt: Event): void { 84 | //解决在设备上拖动到屏幕外面无法触发MOUSE_OUT和MOUSE_UP事件 85 | if (evt.touchId != this._curTouchId) return; 86 | if (!this.visible) { 87 | // 当滑动超过设定的距离时才显示操纵杆 88 | let moveDis: number = this.distanceSquare(this._startStageX, this._startStageY, evt.stageX, evt.stageY); 89 | if (moveDis > this._MaxMoveDistance * this._MaxMoveDistance) { 90 | this.visible = true; 91 | this._isTouchMove = true; 92 | } 93 | } else { 94 | //将移动时的鼠标屏幕坐标转化为摇杆局部坐标 95 | let locationPos: Laya.Point = this.globalToLocal(new Laya.Point(Laya.stage.mouseX, Laya.stage.mouseY), false); 96 | //更新摇杆控制点位置 97 | this.joystickPoint.pos(locationPos.x, locationPos.y); 98 | //更新控制点与摇杆中心点位置距离 99 | this._deltaX = locationPos.x - this._originPiont.x; 100 | this._deltaY = locationPos.y - this._originPiont.y; 101 | //计算控制点在摇杆中的角度 102 | let dx: number = this._deltaX * this._deltaX; 103 | let dy: number = this._deltaY * this._deltaY; 104 | this.angle = Math.atan2(this._deltaX, this._deltaY) * 180 / Math.PI; 105 | if (this.angle < 0) this.angle += 360; 106 | //对角度取整 107 | this.angle = Math.round(this.angle); 108 | //计算控制点在摇杆中的弧度 109 | this.radians = Math.PI / 180 * this.angle; 110 | //强制控制点与中心距离 111 | if (dx + dy >= this._joystickRadius * this._joystickRadius) { 112 | //控制点在半径的位置(根据弧度变化) 113 | let x: number = Math.floor(Math.sin(this.radians) * this._joystickRadius + this._originPiont.x); 114 | let y: number = Math.floor(Math.cos(this.radians) * this._joystickRadius + this._originPiont.y); 115 | this.joystickPoint.pos(x, y); 116 | } 117 | else { 118 | //不超过取原坐标 119 | this.joystickPoint.pos(locationPos.x, locationPos.y); 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * 鼠标抬起事件回调 126 | * @param evt 127 | */ 128 | private _onMouseUp(evt: Event): void { 129 | // 如果不是上次的点击id,返回(避免多点抬起,以第一次按下id为准) 130 | if (evt.touchId != this._curTouchId) return; 131 | this.visible = false; 132 | this._touchRect.off(Event.MOUSE_MOVE, this, this._onMouseMove); 133 | //修改摇杆角度与弧度为-1(代表无角度) 134 | this.radians = this.angle = -1; 135 | } 136 | 137 | /** 138 | * 两点距离的平方 139 | * @param srcX 起始点X值 140 | * @param srcY 起始点Y值 141 | * @param desX 目标点X值 142 | * @param desY 目标点Y值 143 | */ 144 | public distanceSquare(srcX: number, srcY: number, desX: number, desY: number): number { 145 | return (desX - srcX) * (desX - srcX) + (desY - srcY) * (desY - srcY); 146 | } 147 | } -------------------------------------------------------------------------------- /src/Main.ts: -------------------------------------------------------------------------------- 1 | import GameConfig from "./GameConfig"; 2 | import { JoyStick } from "./JoyStick"; 3 | class Main { 4 | public static joystick: JoyStick; 5 | constructor() { 6 | //根据IDE设置初始化引擎 7 | if (window["Laya3D"]) Laya3D.init(GameConfig.width, GameConfig.height); 8 | else Laya.init(GameConfig.width, GameConfig.height, Laya["WebGL"]); 9 | Laya["Physics"] && Laya["Physics"].enable(); 10 | Laya["DebugPanel"] && Laya["DebugPanel"].enable(); 11 | Laya.stage.scaleMode = GameConfig.scaleMode; 12 | Laya.stage.screenMode = GameConfig.screenMode; 13 | //兼容微信不支持加载scene后缀场景 14 | Laya.URL.exportSceneToJson = GameConfig.exportSceneToJson; 15 | 16 | //打开调试面板(通过IDE设置调试模式,或者url地址增加debug=true参数,均可打开调试面板) 17 | if (GameConfig.debug || Laya.Utils.getQueryString("debug") == "true") Laya.enableDebugPanel(); 18 | if (GameConfig.physicsDebug && Laya["PhysicsDebugDraw"]) Laya["PhysicsDebugDraw"].enable(); 19 | if (GameConfig.stat) Laya.Stat.show(); 20 | Laya.alertGlobalError = true; 21 | 22 | //激活资源版本控制,version.json由IDE发布功能自动生成,如果没有也不影响后续流程 23 | Laya.ResourceVersion.enable("version.json", Laya.Handler.create(this, this.onVersionLoaded), Laya.ResourceVersion.FILENAME_VERSION); 24 | } 25 | 26 | onVersionLoaded(): void { 27 | //激活大小图映射,加载小图的时候,如果发现小图在大图合集里面,则优先加载大图合集,而不是小图 28 | Laya.AtlasInfoManager.enable("fileconfig.json", Laya.Handler.create(this, this.onConfigLoaded)); 29 | } 30 | 31 | onConfigLoaded(): void { 32 | //加载IDE指定的场景 33 | GameConfig.startScene && Laya.Scene.open(GameConfig.startScene); 34 | Main.joystick = new JoyStick(Laya.stage); 35 | Laya.stage.addChild(Main.joystick); 36 | } 37 | } 38 | //激活启动类 39 | new Main(); 40 | -------------------------------------------------------------------------------- /src/ui/layaMaxUI.ts: -------------------------------------------------------------------------------- 1 | /**This class is automatically generated by LayaAirIDE, please do not make any modifications. */ 2 | import View=Laya.View; 3 | import Dialog=Laya.Dialog; 4 | import Scene=Laya.Scene; 5 | var REG: Function = Laya.ClassUtils.regClass; 6 | export module ui { 7 | export class joystickViewUI extends Scene { 8 | public joystickBg:Laya.Image; 9 | public joystickPoint:Laya.Image; 10 | constructor(){ super()} 11 | createChildren():void { 12 | super.createChildren(); 13 | this.loadScene("joystickView"); 14 | } 15 | } 16 | REG("ui.joystickViewUI",joystickViewUI); 17 | export class mainViewUI extends Scene { 18 | constructor(){ super()} 19 | createChildren():void { 20 | super.createChildren(); 21 | this.loadScene("mainView"); 22 | } 23 | } 24 | REG("ui.mainViewUI",mainViewUI); 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "noEmitHelpers": true, 6 | "sourceMap": false 7 | }, 8 | "exclude": [ 9 | "node_modules" 10 | ] 11 | } --------------------------------------------------------------------------------