用prestashop做网站,塘下春华网站建设,python在线编辑器,怎样申请网站注册游戏展示 拼图演示 资源#xff1a;
链接#xff1a;https://pan.baidu.com/s/1BGeSmRCO_WZRUyl3MxefGw 提取码#xff1a;0n4a 一、玩法介绍
排列拼图碎片#xff0c;拼出最后的图案。可以点住碎片的任意位置拖动#xff1b;点击重来按钮#xff0c;可以… 游戏展示 拼图演示 资源
链接https://pan.baidu.com/s/1BGeSmRCO_WZRUyl3MxefGw 提取码0n4a 一、玩法介绍
排列拼图碎片拼出最后的图案。可以点住碎片的任意位置拖动点击重来按钮可以回到最初状态重新开始。
二、流畅的拖拽操作
有很多电脑游戏的原型来自于现实世界中的玩具拼图游戏就是其中的一个代表。
本文我们介绍的拼图游戏虽然是一款玩法比较简单的游戏不过这并不意味着开发也非常简单。
相对于其他游戏通过操作键盘或移动鼠标来控制角色的运动方向拼图游戏通过鼠标的拖拽直接移动拼图的碎片。
游戏的核心在于流畅的拖拽操作。
除了拖拽操作外我们也可以借此机会思考一下诸如当碎片移动到正确的位置附近时会被吸附到正确的位置等触屏游戏的小细节。
三、点住碎片的任意位置拖动
Unity可以很容易判断出某个对象受到了点击不过如果要实现自然流畅的操作我们仍需下功夫。这里为了让鼠标的拖拽操作更急接近用手指摁住移动的效果我们需要考虑一下如何才能点住碎片的任意位置拖动。
1、透视变换和逆透视变换
鼠标的光标位于屏幕上时其位置坐标位于二维坐标系内。而拼图碎片位于3D空间内所以其位置坐标自然有三个维度。为了比较鼠标光标和拼图碎片的位置必须将它们放入相同的坐标系。因此我们使用逆透视变换的方法将鼠标光标的坐标变换至三维坐标系
2、被点击处即为光标的位置
通过逆透视变换将鼠标光标和拼图碎片的坐标统一到相同的坐标系后我们就该尝试通过拖拽使拼图移动了只需要在点击按键的瞬间将鼠标光标的坐标复制到拼图碎片的坐标即可。这种方法确实非常简单不过它有个缺点鼠标光标总是显示在拼图碎片的中心。
在本游戏中拼图碎片被点击的位置并不影响游戏的玩法。不过对于某些游戏而言点击位置的不同可能会改变角色的朝向或者是游戏角色以光标为中心摆动这些情况下在何处点击就变得很重要了。
而且即使不影响游戏的核心玩法点击的瞬间拼图碎片会突然移动一下这种体验也很糟糕。尽管有些时候这种机制可能会更好但是为了应对不同的要求我们还是需要掌握如何能点住碎片的任意处拖动。
在本游戏中碎片的点击判断都是通过Unity的网格碰撞器实现的。网格碰撞器采用网格进行碰撞检测点击拼图碎片的任何部位都将发生碰撞。对于玩家来说点击碎片的哪个位置都可以这反映到程序中就是不用关心碎片的何处受到了点击。
点击的瞬间鼠标光标不一定位于碎片的中心。两者的坐标存在一定的差距我们将这种坐标的差距称为偏移。
之前我们把光标的坐标原原本本地复制到碎片坐标时因为两个坐标值相同所以差距为0这种坐标差的急剧变化正是导致拼图碎片突然移动的原因。
知道了坐标偏移值的变化是问题所在后我们来考虑如何固定这个偏移值。首先要在鼠标点击拼图碎片的瞬间也就是开始拖动的时候计算出鼠标光标和碎片中心的坐标差得到的值就是偏移值。
偏移碎片的位置-鼠标光标的位置
拖动的过程中则与之相反用鼠标光标的位置加上偏移值就可以得到碎片的位置
碎片的位置鼠标光标位置偏移值
这样一来鼠标光标距离碎片中心总是保持一定的距离这样就保证了鼠标点击瞬间的位置就是碎片被拖拽的位置。
下面来看看实际的代码
private void begin_dragging(){do {// 将光标坐标变换为3D空间内的世界坐标Vector3 world_position;if(!this.unproject_mouse_position(out world_position, Input.mousePosition)) {break;}if(PieceControl.IS_ENABLE_GRAB_OFFSET) {// 求出偏移值点击位置距离碎片的中心有多远this.grab_offset this.transform.position - world_position;}} while(false);}
private void do_dragging(){do {// 将光标坐标变换为3D空间内的世界坐标Vector3 world_position;if(!this.unproject_mouse_position(out world_position, Input.mousePosition)) {break;}// 加上光标坐标3D的偏移值计算出碎片的中心坐标this.transform.position world_position this.grab_offset;} while(false);}
四、打乱拼图碎片
商店里售卖纸质拼图游戏时一般会将拼图碎片打乱顺序后放入包装盒中。虽然也有些是已经拼好的状态不过玩家在开始游戏之前还是要将各碎片的顺序打乱。
有很多事情都是人类做起来很简单计算机处理起来却很难比如将拼图碎片全部打乱这件事就是一个例子。
Unity提供了取得随机数的方法不过单纯使用该方法似乎并不能达到打乱碎片顺序的目的。
这里我们不妨来分析一下如何随机打乱各拼图碎片的顺序。
1、设置拼图碎片的坐标为随机数
最简单的随机打乱拼图碎片的方法是直接将随机数代入各个碎片的坐标。只要控制好随机数的范围就能让各个拼图碎片随机分布在画面上。
但这种方式的弊端也很明显就是有很多拼图碎片可能叠在一起。虽然这样也未尝不可不过可以的话最好还是将各碎片均匀分散开。如果很多碎片重叠在一起就可能导致下面的碎片被覆盖而无法看见。
2、改进策略
首先我们整理一下拼图碎片随机散开的要求即需求分析 碎片之间彼此互不重叠碎片散开分布到整个画面上随机分散各个碎片 需求基本上就是这样。如果拼图碎片的数量有所增加可能还需要追加一项能够控制游戏的难易度。
接下来我们对实现方法进行说明。首先简单熟悉一下整体流程 将拼图碎片分配到网格中打乱拼图碎片的排列顺序在网格内通过随机坐标调整碎片的位置将整个拼图随机旋转一定角度 我们可以选择任意图片将其分割成几块。这里我们选择一个猫头鹰图片将其分割成8块。
首先将所有的拼图碎片从左上角开始依次放入网格中。
该网格的行数和列数相同并且网格总数达于拼图碎片数量。猫头鹰拼图碎片数量为8我们就搞一个3✖️3的网格空出来的格子不用理会。根据碎片数量的不同有时候剩余的格子会比较多这种情况下可以调整网格的行数和列数。
所有网格块都为正方形且都应当能确保能够容纳下拼图碎片。另外因为后续步骤在网格内移动拼图碎片所以还需要在确保整体网格不溢出画面的前提下适当放大网格的尺寸。
之所以想这样把拼图碎片纺织到网格中是为了避免出现碎片之间彼此重叠的状况。
接下来随机打乱个碎片的排列位置
在第一个步骤中我们将碎片从左上角开始依次放入了网格中。而第二个步骤就是打乱各个碎片的排列顺序。利用随机数选出两个网格案后交换其中的碎片空白的网格也可以参与交换。
做到这里前面我们做出的需求分析中碎片之间彼此互不重叠碎片分散于整个画面和随机分散各个碎片就已经基本得到了实现。不过从程序实际情况来看很容易发现拼图碎片被规则地排列在了网格上。我们得想办法让这种随机分散的效果更真实。
在第三个步骤中我们让拼图碎片在网格中随机移动。
最初的步骤中增加网格尺寸的用意就在于为这里的碎片移动做准备。如果网格的尺寸太小将无法移动碎片反之如果太大则会令碎片之间过于松散。我们需要结合拼图碎片的大小和画面整体的尺寸调整网格尺寸为最佳值
最后为了不让玩家看出碎片排列的规律性稍微将拼图网格整体旋转一定的角度
虽然旋转了整体的网格但是需要保持拼图碎片自身的角度不变。
下面我们结合实际代码来看看这一流程
private void shuffle_pieces(){#if true// 将碎片按照网格顺序排列int[] piece_index new int[this.shuffle_grid_num*this.shuffle_grid_num];for(int i 0;i piece_index.Length;i) {if(i this.all_pieces.Length) {piece_index[i] i;} else {piece_index[i] -1;}}// 随机选取两个碎片交换位置for(int i 0;i piece_index.Length - 1;i) {int j Random.Range(i 1, piece_index.Length);int temp piece_index[j];piece_index[j] piece_index[i];piece_index[i] temp;}// 通过位置的索引变换为实际的坐标来进行配置Vector3 pitch;pitch this.shuffle_zone.size/(float)this.shuffle_grid_num;for(int i 0;i piece_index.Length;i) {if(piece_index[i] 0) {continue;}PieceControl piece this.all_pieces[piece_index[i]];Vector3 position piece.finished_position;int ix i%this.shuffle_grid_num;int iz i/this.shuffle_grid_num;position.x ix*pitch.x;position.z iz*pitch.z;position.x this.shuffle_zone.center.x - pitch.x*(this.shuffle_grid_num/2.0f - 0.5f);position.z this.shuffle_zone.center.z - pitch.z*(this.shuffle_grid_num/2.0f - 0.5f);position.y piece.finished_position.y;piece.start_position position;}// 逐步网格的格子内随机移动位置Vector3 offset_cycle pitch/2.0f;Vector3 offset_add pitch/5.0f;Vector3 offset Vector3.zero;for(int i 0;i piece_index.Length;i) {if(piece_index[i] 0) {continue;}PieceControl piece this.all_pieces[piece_index[i]];Vector3 position piece.start_position;position.x offset.x;position.z offset.z;piece.start_position position;//offset.x offset_add.x;if(offset.x offset_cycle.x/2.0f) {offset.x - offset_cycle.x;}offset.z offset_add.z;if(offset.z offset_cycle.z/2.0f) {offset.z - offset_cycle.z;}}// 使全体旋转foreach(PieceControl piece in this.all_pieces) {Vector3 position piece.start_position;position - this.shuffle_zone.center;position Quaternion.AngleAxis(this.pazzle_rotation, Vector3.up)*position;position this.shuffle_zone.center;piece.start_position position;}this.pazzle_rotation 90;#else// 简单地使用随机数来决定坐标时的情况foreach(PieceControl piece in this.all_pieces) {Vector3 position;Bounds piece_bounds piece.GetBounds(Vector3.zero);position.x Random.Range(this.shuffle_zone.min.x - piece_bounds.min.x, this.shuffle_zone.max.x - piece_bounds.max.x);position.z Random.Range(this.shuffle_zone.min.z - piece_bounds.min.z, this.shuffle_zone.max.z - piece_bounds.max.z);position.y piece.finished_position.y;piece.start_position position;}#endif}