全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-07-25_业务代码写累了,写个小游戏玩玩

您的位置:首页 >> 新闻 >> 行业资讯

业务代码写累了,写个小游戏玩玩 点击关注公众号,“技术干货”及时达!前言工作要劳逸结合,人的天性是好逸恶劳。工作干累了,就给自己找点乐子,调节一下疲惫的身心状态。找什么乐子呢? 玩几把游戏,可是玩游戏有边际递减效应,前面玩的很开心,后面开心的劲头会越来越小。那这个时候该干什么呢?平常玩的游戏都是别人开发的,作为程序员,我们是有能力自己开发一个游戏的。何不自己开发一款小游戏,既能自娱自乐,又能提高编程技能,一举两得,何乐而不为。说干就干,笔者爱玩射击类的单机游戏,那就开发一个射击类的游戏吧。先演示一下最终的效果。 效果演示游戏的玩法是:鼠标点击游戏界面之后,就能以第一人称视角通过鼠标自由地移动和旋转射击角度,按下ESC键,退出鼠标指针锁定功能。按上下左右四个方向键,可以向前后左右四个方向移动,按下空格键,会开火射击,击中目标时,会产生爆炸效果,增加射中分值。 射击类游戏的界面交互一般有五个要素: 一是射击场景二是射击目标三是射击玩家四是射击轨迹五是击中目标后的爆炸效果我们就依次开发这5个要素的功能,并把它们有机的整合在一起,构成一个完整的射击游戏。 创建射击场景threeJS的基础入门知识可参照笔者以前写的这篇文章初探神秘的Three.js。一个典型的 Three.js 程序至少要包括渲染器(Renderer)、场景(Scene)、照相机(Camera), 以及向场景中加入的物体。依次创建渲染器,场景,照相机。并监听浏览器的窗口尺寸改变事件,在窗口大小变化时,调整摄像机的视角和渲染器的大小,确保场景在窗口大小变化时能够正确渲染。 constrenderer=newTHREE.WebGLRenderer(); constscene=newTHREE.Scene(); constcamera=newTHREE.PerspectiveCamera(90,window.innerWidth/window.innerHeight,0.1,1000); adjustWindowSize(); //窗口大小调整事件 window.addEventListener("resize",adjustWindowSize); functionadjustWindowSize(){ camera.aspect=window.innerWidth/window.innerHeight; renderer.setSize(window.innerWidth,window.innerHeight); document.body.appendChild(renderer.domElement); renderer.render(scene,camera); } 向场景中加入地面和灯光「地面 (floor)」 :在游戏或场景中,地面不仅是视觉上的背景,还可以作为其它物体运动的平台。在射击游戏中,地面是玩家和射击目标移动的区域。 「灯光 (light)」 :灯光是场景中的光源,能够照亮物体并产生阴影。在 Three.js 中,方向光 (DirectionalLight) 是一种能够模拟太阳光线的光源,它可以使场景中的物体产生明暗变化,增强场景的真实感和视觉效果。 //创建地面 constfloorGeometry=newTHREE.PlaneGeometry(100,100); constfloorMaterial=newTHREE.MeshBasicMaterial({color:0x808080,side:THREE.DoubleSide constfloor=newTHREE.Mesh(floorGeometry,floorMaterial); floor.rotation.x=-Math.PI/2; scene.add(floor); //添加灯光 constlight=newTHREE.DirectionalLight(0xffffff,1); light.position.set(5,10,7.5); scene.add(light); 创建射击目标下面这段代码创建了一个目标物并将其添加到场景中,为每个目标物赋予随机颜色和随机运动方向,并确保目标物不会相互重叠在一起。 这句代码const target = new THREE.Mesh(targetGeometry, targetMaterial); 创建了一个网格,将几何形状和材质组合在一起,形成一个可渲染的对象。 while循环中的这行代码target.position.set(Math.random() * 100 - 50, 1, Math.random() * 100 - 50);将目标物随机放置在 X 和 Z 轴上,范围是 -50 到 50,Y 轴固定为 1。Three.JS采用的是右手坐标系,X 和 Z 轴方向如下图所示: while循环中的for 循环遍历所有已存在的目标物,如果当前目标物与任何一个已存在的目标物距离小于 5,则认为位置无效,重新生成位置。这样做是为了防止生成的目标值堆叠在一起。 这行代码target.velocity = new THREE.Vector3((Math.random() - 0.5) * 0.1, 0, (Math.random() - 0.5) * 0.1);为目标物设置一个随机速度,使其在 X 和 Z 轴上随机移动,速度范围在 -0.05 到 0.05 之间。 //创建射击目标 functioncreateTarget(){ consttargetGeometry=newTHREE.BoxGeometry(2,2,2); consttargetMaterial=newTHREE.MeshBasicMaterial({ color:newTHREE.Color(Math.random(),Math.random(),Math.random()), consttarget=newTHREE.Mesh(targetGeometry,targetMaterial); letvalidPosition=false; while(!validPosition){ target.position.set(Math.random()*100-50,1,Math.random()*100-50); validPosition=true; for(leti=0;itargets.length;i++){ constotherTarget=targets[i]; if(target.position.distanceTo(otherTarget.position)5){ //检查距离 validPosition=false; break; } } } target.velocity=newTHREE.Vector3((Math.random()-0.5)*0.1,0,(Math.random()-0.5)*0.1); targets.push(target); scene.add(target); } 更新射击目标位置射击目标创建之后,需要在每个渲染帧更新目标物的位置,使它们在场景中延续原来的方向继续移动,当目标物移动到地面边界时,要进行回拉。具体步骤为: 遍历每个目标物。根据其速度向量更新位置。检查是否超出地面边界,如果超出,则反转相应轴的速度方向,使其在边界处反弹。//更新目标物位置 functionupdateTargets(){ targets.forEach((target)={ target.position.add(target.velocity); //简单的边界处理,使目标物在地面内移动 if(target.position.x50||target.position.x-50)target.velocity.x=-target.velocity.x; if(target.position.z50||target.position.z-50)target.velocity.z=-target.velocity.z; } 更新玩家位置先创建一个指针锁定控制器,PointerLockControls 用于将鼠标指针锁定到浏览器窗口中,使得玩家可以使用鼠标控制相机的方向。当用户点击游戏页面时,调用 controls.lock() 方法将鼠标指针锁定。 constcontrols=newTHREE.PointerLockControls(camera,document.body); document.addEventListener("click",()=controls.lock()); 接着监听方向键和空格键的按下弹起事件: onKeyDown 事件回调处理玩家按下键盘方向键和空格键时的事件,根据按下的方向键,将相应方向的移动标志位标记为需要移动。如果按下空格键,调用 shoot() 函数(后面会讲)进行射击。 onKeyUp 事件回调处理玩家松开键盘方向键时的事件。根据弹起的方向键,将相应方向的移动标志位标记为停止移动。 //处理玩家移动 constmove={forward:false,backward:false,left:false,right:false //处理四个方向键和空格键按下事件 constonKeyDown=function(event){ switch(event.code){ case"ArrowUp": move.forward=true; break; case"ArrowLeft": move.left=true; break; case"ArrowDown": move.backward=true; break; case"ArrowRight": move.right=true; break; case"Space": shoot(); break; } }; //处理四个方向键弹起事件 constonKeyUp=function(event){ switch(event.code){ case"ArrowUp": move.forward=false; break; case"ArrowLeft": move.left=false; break; case"ArrowDown": move.backward=false; break; case"ArrowRight": move.right=false; break; } }; document.addEventListener("keydown",onKeyDown); document.addEventListener("keyup",onKeyUp); updatePlayer 函数用于更新玩家位置。内部创建了一个 direction 向量,用于存储玩家移动的方向和距离。 根据 move 对象的标记值,调整 direction 向量的值。使用 controls.moveRight 和 controls.moveForward 方法将玩家移动到新的位置。 //更新玩家位置 functionupdatePlayer(){ constspeed=0.1; constdirection=newTHREE.Vector3(); if(move.forward)direction.z+=speed; if(move.backward)direction.z-=speed; if(move.left)direction.x-=speed; if(move.right)direction.x+=speed; controls.moveRight(direction.x); controls.moveForward(direction.z); } 串起来就是通过 PointerLockControls 实现用鼠标控制玩家在 3D 场景中的移动。通过监听键盘按键事件,标记玩家的移动方向,并在每一帧中根据玩家的移动方向更新玩家的位置(这个方法会放在requestAnimationFrame中执行),这样就使得玩家可以使用鼠标和方向键在场景中移动,使用空格键进行射击。 射击处理当按下空格键时,要绘制射出子弹的效果。子弹要冒火光,飞行轨迹看起来像抛物线。具体实现是: 创建一个小球形状的子弹 (SphereGeometry)。设置子弹的材质为黄色 (MeshBasicMaterial)。根据当前相机的位置设置子弹的位置 (position.copy(controls.getObject().position)),即从玩家位置发射。设置子弹的初始速度 (velocity) 和方向。applyQuaternion(camera.quaternion) 将速度方向旋转到相机的方向。给子弹添加重力 (gravity),使子弹在飞行过程中受到重力影响。将子弹添加到 bullets 数组中,并添加到场景中。之所以需要一个bullets 数组进行存储,是因为发射出去的子弹并不是仅有一颗,而且后续在每个渲染帧,要持续更新。所以在添加到场景之后,要将子弹的状态保存起来。functionshoot(){ constbulletGeometry=newTHREE.SphereGeometry(0.1,8,8); constbulletMaterial=newTHREE.MeshBasicMaterial({color:0xffff00 constbullet=newTHREE.Mesh(bulletGeometry,bulletMaterial); bullet.position.copy(controls.getObject().position); bullet.velocity=newTHREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion); bullet.gravity=newTHREE.Vector3(0,-0.001,0);//添加重力 bullets.push(bullet); scene.add(bullet); } 更新子弹位置子弹射出去之后,子弹的位置要持续更新。当子弹超出一定距离时,让子弹消失在视野中。具体实现步骤为: 遍历所有子弹,更新子弹的位置。将重力作用添加到子弹的速度上 (bullet.velocity.add(bullet.gravity))。更新子弹的位置 (bullet.position.add(bullet.velocity))。如果子弹的位置超过一定距离(例如 100),则从场景中移除子弹,并从 bullets 数组中删除。使用 filter 方法过滤掉超过距离的子弹。functionupdateBullets(){ bullets=bullets.filter((bullet)={ bullet.velocity.add(bullet.gravity);//更新速度 bullet.position.add(bullet.velocity); if(bullet.position.length()=100){ scene.remove(bullet); returnfalse; } returntrue; } 检测是否击中目标子弹射出之后,要比较子弹和射击目标的距离,判断是否击中目标。击中目标后,要有爆炸效果,并且需要移除子弹和击中目标,重新创建新的射击目标,并更新射击分值。实现步骤为: 遍历所有目标物 (targets) 和子弹 (bullets)。计算目标物与子弹之间的距离 (distanceTo)。如果距离小于1,即认为子弹击中了目标物。执行击中目标逻辑。functioncheckHit(){ targets.forEach((target,tIndex)={ bullets.forEach((bullet,bIndex)={ constdistance=target.position.distanceTo(bullet.position); if(distance1){ createExplosion(target.position); scene.remove(target); scene.remove(bullet); targets.splice(tIndex,1); bullets.splice(bIndex,1); score+=10; scoreElement.innerText=`分数:${score}`; createTarget();//创建新的目标物 } } 创建爆炸效果目标被击中后,要产生爆炸效果,展示100ms后,移除爆炸元素。爆炸效果的实现步骤是: 通过一个粒子系统(PointsMaterial)来模拟爆炸效果,设置粒子的材质、纹理,大小、颜色、隐藏等。你可以创建BufferGeometry一个THREE.Points对象,将随机生成的粒子添加到其所在的场景中。使用setTimeout在100毫秒后将样本从场景中移除。//预加载爆炸纹理 constexplosionTexture=newTHREE.TextureLoader().load("./expose.jpg"); //爆炸效果 functioncreateExplosion(position){ constexplosionMaterial=newTHREE.PointsMaterial({ size:1, map:explosionTexture, blending:THREE.AdditiveBlending, depthWrite:false, transparent:true, color:0xff4500, constexplosionGeometry=newTHREE.BufferGeometry(); constvertices= for(leti=0;i100;i++){ constparticle=newTHREE.Vector3(); particle.x=position.x+(Math.random()-0.5)*5; particle.y=position.y+(Math.random()-0.5)*5; particle.z=position.z+(Math.random()-0.5)*5; vertices.push(particle.x,particle.y,particle.z); } explosionGeometry.setAttribute("position",newTHREE.Float32BufferAttribute(vertices,3)); constexplosion=newTHREE.Points(explosionGeometry,explosionMaterial); scene.add(explosion); setTimeout(()={ scene.remove(explosion); },100); } 这里要说明一下使用THREE.TextureLoader预加载爆炸效果的纹理,如果进行预加载,首次击中目标时没有爆炸效果。 整合要素前面已经实现了目标游戏的领导力要素,现在需要对各个要素进行社区的整合。 1.是初始化游戏环境。创建游戏场景,向游戏场景中添加地面,灯光,射击目标,玩家,监听方向键和空格键的单击弹起事件,当事件发生时,执行相应的行为。动画的第二循环,不断地更新场景中访客对象(玩家、子弹、目标物),并进行击中检测,然后将更新后的场景出来,从而实现动态的效果。init(); //初始化 functioninit(){ for(leti=0;i20;i++){ createTarget(); } animate(); } functionanimate(){ requestAnimationFrame(animate); updatePlayer(); updateBullets(); updateTargets(); checkHit(); renderer.render(scene,camera); } 到此,就实现了文章开头展示的有趣游戏效果。 最后开发这个游戏之后,有感触,第一点激活使用ThreeJS开发游戏的要点是UI要素的生成,这需要进行复杂的UI建模,我还没有从网上找到核心的生成方式。 第二点激活自己开发的游戏,这也是一种自我牺牲。因为我平时很少玩,所以玩的快乐阈值很低。如果你经常玩王者荣耀这种制作精良的游戏,你会很高兴的,因为你一定会获得很多快乐。如果你喜欢小游戏,你会很高兴的,你会很开心的。 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2019-03-04_男孩女孩为爱“换脸”,竟是为了…… 下一篇:2022-09-13_「转」浪潮所向:第19届CTR洞察高峰论坛即将开启 | 9.15 北京

TAG标签:

16
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价