Pixi.js学习
记录学习pixi时候遇到的难一点的地方,我个人觉得我还是学得比较快的。
1.官方文档解释toLocal函数的时候,解释的不是很清楚,这里引文一篇博客加以解释:here
个人理解:按照anysprite.toLocal(sprite_x,sprite_y)的参数形式来解释会——整个所用到的都是全局坐标,比较 anysprite与以sprite_x为基础偏移sprite_y坐标,返回坐标差值
另外anySprite.toGlobal(postion)函数,是将anySprite的顶层容器的全局坐标计算的初始坐标原点放在postion,返回此时的anySprite的坐标。
还有很重要一件事,如果对anySprite的大小有设置,那么会对toLocal和toGlobal函数都会造成影响,具体影响值是设定大小与原图像比例进行等比变化——比如使用toGlobal(pos),如果设定为width=100,那么偏移量放大100倍pos.x,但是原图像又是按照100的数值来进行绘制,属实迷惑了。。。

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; } </style> <script src="https://cdn.bootcss.com/pixi.js/5.2.1/pixi.min.js"></script> <script src="../../js/vue.min.js"></script> </head> <body> <script> //判断浏览器渲染引擎 //Aliases 别名 let Application = PIXI.Application, loader = PIXI.loader, TextureCache = PIXI.TextureCache, resources = PIXI.loader.resources, Sprite = PIXI.Sprite; let Rectangle = PIXI.Rectangle; const resolution = window.devicePixelRatio; let app = new Application({ width: 600, height: 600, resolution: 1, // default: 1 分辨率 forceCanvas: true, //强制使用canvas引擎绘制 backgroundColor: 0x1099bb, //背景颜色 }); document.body.appendChild(app.view); let t1 = PIXI.Texture.from("./resources/三人.png") // tolocal演示 let sp1 = new PIXI.Sprite(t1); sp1.position.set(100, 100); console.log(sp1.width); console.log(sp1.height); // sp1.width=503;//这里是我是用的文件原像素大小 // sp1.height=752; // sp1.width=100;//设置比例, // sp1.height=100; let pt1 = new PIXI.Point(40, 40); // 这里多搞几层容器 let c1 = new PIXI.Container() c1.addChild(sp1); c1.position.set(100, 100) app.stage.addChild(c1); let r1 = sp1.toGlobal(pt1) // 第二个第三个参数可以省略 console.log(r1) // Point2 {x: 240, y: 240} // 正常值应该是240,但是设定过高宽后,变为了100+width*40,100+height*40 </script> </body> </html>
2.在游戏状态部分,对于角色的移动官方样例做了点优化,允许同时x,y轴移动且不冲突,同时,对代码也进行了封装优化:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; } </style> <script src="https://cdn.bootcss.com/pixi.js/5.2.1/pixi.min.js"></script> <script src="../../js/vue.min.js"></script> </head> <body> <script> //判断浏览器渲染引擎 //Aliases 别名 let Application = PIXI.Application, loader = PIXI.loader, TextureCache = PIXI.TextureCache, resources = PIXI.loader.resources, Sprite = PIXI.Sprite; let Rectangle = PIXI.Rectangle; const resolution = window.devicePixelRatio; let app = new Application({ width: 400, height: 400, resolution: resolution, // default: 1 分辨率 forceCanvas: true, //强制使用canvas引擎绘制 backgroundColor: 0x1099bb, //背景颜色 }); document.body.appendChild(app.view); //加载图像方式 loader .add("./resources/三人.json") .on("progress", loadProgressHandler) .load(setup); // 监控加载进度 function loadProgressHandler(loader, resource) { console.log("loading: " + resource.url); console.log("progress: " + loader.progress + "%"); } var sprite, state; state = stop; let left = keyboard("ArrowLeft"), up = keyboard("ArrowUp"), right = keyboard("ArrowRight"), down = keyboard("ArrowDown"); function setup() { //单一创建 console.log("All files loaded"); // 异步处理 //读取纹理创建精灵 let texture = resources["./resources/三人.json"].textures["角色3.png"]; sprite = new Sprite(texture); sprite.position.set(300, 50); sprite.width = 50; sprite.height = 50; sprite.movex = 0; sprite.movey = 0; app.stage.addChild(sprite); setkeyboard(); app.ticker.add(delta => gameLoop(delta)); } function gameLoop(delta) { // sprite.x += 1; state(delta); } function stop() { // } function move() { sprite.x += sprite.movex; sprite.y += sprite.movey; } function keyboard(value) { let key = {}; key.value = value; key.isDown = false; key.isUp = true; key.press = undefined; key.release = undefined; //按下 key.downHandler = event => { if (event.key === key.value) { if (key.isUp && key.press) key.press(); key.isDown = true; key.isUp = false; event.preventDefault(); } } //按后 key.upHandler = event => { if (event.key === key.value) { if (key.isDown && key.value) key.release(); key.isDown = false; key.isUp = true; event.preventDefault(); } } const downListener = key.downHandler.bind(key); const upListener = key.upHandler.bind(key); window.addEventListener( "keydown", downListener, false ); window.addEventListener( "keyup", upListener, false ); key.unsubscribe = () => { window.removeEventListener("keydown", downListener); window.removeEventListener("keyup", upListener); }; return key; } function setkeyboard() { setkey(left, right, -1, 0); setkey(right, left, 1, 0); setkey(up, down, 0, -1); setkey(down, up, 0, 1); } function setkey(key, rkey, x, y) { key.press = () => { sprite.movex = x ? x : sprite.movex; sprite.movey = y ? y : sprite.movey; state = move; } key.release = () => { if (x != 0 && sprite.movex == x) sprite.movex = 0; if (y != 0 && sprite.movey == y) sprite.movey = 0; if (sprite.movex == 0 && sprite.movey == 0) state = stop; } } </script> </body> </html>
解释:
1.初始将state定义为stop空函数
2.在按键后将sprite的movex,movey改变,同时切换到move函数
3.封装了setkey,对按键设置进行优化
按键后,对当前非0指定方向变量进行赋值,同时切换move
按起,对当前非0指定方向进行考虑,如果当前实际move值与当前按键指向相同,那么初始化move方向,同时,如果全部为0,那么切换到stop。
记录一份官网的寻宝猎人demo(自己的优化版本:1.碰撞时停1s;2.xy双轴同时移动)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; } </style> <script src="https://cdn.bootcss.com/pixi.js/5.2.1/pixi.min.js"></script> <script src="https://www.kkkk1000.com/js/dust.js"></script> <script src="../js/vue.min.js"></script> </head> <body> <script> //判断浏览器渲染引擎 const type = PIXI.utils.isWebGLSupported() ? "WebGL" : "canvas"; //console.log(type);//WebGL // type console.log("🚀 ~ file: demo.html ~ line 15 ~ type", type) PIXI.utils.sayHello(type); let d = new Dust(PIXI); const width = window.innerWidth; const height = window.innerHeight; const resolution = window.devicePixelRatio; let app = new PIXI.Application({ width: width, // default: 800 宽度 height: height, // default: 600 高度 resolution: resolution, // default: 1 分辨率 forceCanvas: true, //强制使用canvas引擎绘制 backgroundColor: 0x1099bb, //背景颜色 antialias: true, // default: false 反锯齿 }); //获取舞台 let stage = app.stage; //获取渲染器 let renderer = app.renderer; document.body.appendChild(app.view); // 所有形状的绘制,都先从创建Pixi的Graphics类(Pixi.Graphics)的实例开始。 let Graphics = PIXI.Graphics; let Text = PIXI.Text; let TextStyle = PIXI.TextStyle; // 矩形 let rectangle = new Graphics(); rectangle.beginFill(0x66CCFF); //填入颜色 rectangle.lineStyle(4, 0xFF3300, 1); //外边框(像素宽度,颜色,透明度) rectangle.drawRect(0, 0, 100, 100); //进行矩形绘制 rectangle.endFill(); // 结束绘制 rectangle.x = 170; rectangle.y = 170; // app.stage.addChild(rectangle); // 文本 let message = new Text(""); message.position.set(0, 0); // app.stage.addChild(message); //主角 let p1 = PIXI.Texture.from("../pixi/sprites(精灵)/imgs/角色3.png"); let sprite = new PIXI.Sprite(p1); sprite.position.set(100, 100); sprite.width = 50; sprite.height = 50; sprite.movex = 0; sprite.movey = 0; // app.stage.addChild(sprite); //目标点 let p2 = PIXI.Texture.from("../pixi/sprites(精灵)/imgs/角色2.png"); let sprite2 = new PIXI.Sprite(p2); sprite2.position.set(300, 300); sprite2.width = 50; sprite2.height = 50; // app.stage.addChild(sprite2); var gamesence = new PIXI.Container(); var gameover = new PIXI.Container(); let message2 = new Text("GameOver , You Win"); message2.position.set(200, 200); gamesence.addChild(rectangle, message, sprite2, sprite); gameover.addChild(message2); gameover.visible = false; app.stage.addChild(gamesence); app.stage.addChild(gameover); var state, flag; var int; state = stop; flag = true; let left = keyboard("ArrowLeft"), up = keyboard("ArrowUp"), right = keyboard("ArrowRight"), down = keyboard("ArrowDown"); setkeyboard(); startgame(); function startgame() { int = setInterval(() => { gameLoop(); }, 1000 / 60); } async function gameLoop() { // sprite.x += 1; console.log("gameloop"); state(); if (hitTestRectangle(sprite, rectangle)) { rectangle.tint = 0xff3300; // state = stop; //停止移动,关闭按键 message.text = "碰撞! 一秒后返回原点"; flag = false; console.log("碰撞了"); clearInterval(int); setTimeout(() => { //恢复 flag = true; console.log("恢复"); sprite.position.set(100, 100); startgame(); }, 1000); } else if (hitTestRectangle(sprite, sprite2)) { message.text = "win!"; clearInterval(int); setTimeout(() => { gamesence.visible = false; gameover.visible = true; }, 1000); } else { message.text = "正常移动"; rectangle.tint = 0xccff99; } } function stop() { // } function move() { if (!flag) return; sprite.x += sprite.movex; sprite.y += sprite.movey; } function keyboard(value) { let key = {}; key.value = value; key.isDown = false; key.isUp = true; key.press = undefined; key.release = undefined; //按下 key.downHandler = event => { if (event.key === key.value) { if (key.isUp && key.press) key.press(); key.isDown = true; key.isUp = false; event.preventDefault(); } } //按后 key.upHandler = event => { if (event.key === key.value) { if (key.isDown && key.value) key.release(); key.isDown = false; key.isUp = true; event.preventDefault(); } } const downListener = key.downHandler.bind(key); const upListener = key.upHandler.bind(key); window.addEventListener( "keydown", downListener, false ); window.addEventListener( "keyup", upListener, false ); key.unsubscribe = () => { window.removeEventListener("keydown", downListener); window.removeEventListener("keyup", upListener); }; return key; } function setkeyboard() { setkey(left, right, -1, 0); setkey(right, left, 1, 0); setkey(up, down, 0, -1); setkey(down, up, 0, 1); } function setkey(key, rkey, x, y) { key.press = () => { sprite.movex = x ? x : sprite.movex; sprite.movey = y ? y : sprite.movey; state = move; } key.release = () => { if (x != 0 && sprite.movex == x) sprite.movex = 0; if (y != 0 && sprite.movey == y) sprite.movey = 0; if (sprite.movex == 0 && sprite.movey == 0) state = stop; } } function hitTestRectangle(r1, r2) { //Define the variables we'll need to calculate let hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //hit will determine whether there's a collision hit = false; //Find the center points of each sprite r1.centerX = r1.x + r1.width / 2; r1.centerY = r1.y + r1.height / 2; r2.centerX = r2.x + r2.width / 2; r2.centerY = r2.y + r2.height / 2; //Find the half-widths and half-heights of each sprite r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; //Calculate the distance vector between the sprites vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; //Figure out the combined half-widths and half-heights combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; //Check for a collision on the x axis if (Math.abs(vx) < combinedHalfWidths) { //A collision might be occurring. Check for a collision on the y axis if (Math.abs(vy) < combinedHalfHeights) { //There's definitely a collision happening hit = true; } else { //There's no collision on the y axis hit = false; } } else { //There's no collision on the x axis hit = false; } //`hit` will be either `true` or `false` return hit; }; </script> </body> </html>
【Over】
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!