Unity知识三:两种方式实现切水果的刀痕

Posted 博士装呗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity知识三:两种方式实现切水果的刀痕相关的知识,希望对你有一定的参考价值。

Unity作为游戏开发平台之一,还是有很多实用的小技巧的,今天来学习一下怎样用两种方式来显示切水果游戏中的刀痕:
1.正常显示下的刀痕:
什么叫正常显示下的呢?我们所接触过的切水果游戏一般都是2D游戏,那我们知道,2D游戏可以用Unity直接来做,还可以使用NGUI、UGUI或者其他方法通过UI来实现。
所以我们第一种方法就是不借助UI来做。
首先来看看我们刀痕的素材:(需要的同学可以右键另存。^_^)

打开Unity:

新建一个空游戏体,命名为“BackGround”,然后在组件面板中添加组件GUI Texture:(创建出来的物体不要忘记reset一下哦~)

找到Texture属性,点击后面的圆圈,为它选择一个背景图片:

背景素材:

添加好背景图片之后,我们看Scenes面板和Game面板,发现了两个问题:

1.在Scenes面板中,添加上去的图片看不到;
2.Game面板中图片的位置有点不对。

Scenes中看不到的情况,由于我也是小白,搞不懂原理,但是我猜是因为GUITexture是UI的一部分,所以在Scenes面板中有时候显示效果不好,至于怎么设置去让它显示出来,我暂时还不知道,我现在是以Game面板上的显示情况做参考的。

Game面板下显示不对,我们把它Transform下的Position设置为(0.5,0.5,0):

这样就可以了。

接下来我们再新建一个空游戏体,将其命名为Knife,并为其添加LineRenderer组件:

我们在上面右图中可以找到Materials属性,下面有Size和Element 0.
因为Materials是一个Materials类型的数组,Size是数组的长度,Element是数组元素的值,此时Size为1,那么它就只有一个元素,索引值为0,Element 0因此而来。

我们刚刚创建的游戏体是用来显示刀痕的,那么我们就要把上面我们的刀片素材给它。
大家会发现,直接把图片拖给Element 0不成功,因为Element 0是Material类型的,而图片还不是材质,它现在只是一张图片,对应的材质还没有生成。
我们直接把图片拖给Knife游戏体,Unity会自动为这张图片创建一个材质Material,并把这个材质赋给Element 0;

大家可以在Scenes面板中看到它。

接下来,我们找到Line Renderer下的position属性,发现它也是一个数组,这个数组是干什么用的呢?他就是用来画出Line Renderer的关键属性。

实际上Line Renderer是这样的:你给我几个坐标,我把相应的材质在几个坐标之间渲染出来。现在:Element 0为(0,0,0)Element 1为(0,0,1),那就是说,把图片在坐标(0,0,0)和(0,0,1)之间显示出来,因为两点确定一条直线嘛~对不对?

既然我们了解了Line Renderer的原理,那么我们就知道了,我想用鼠标画出一条线,我只要获取到鼠标左键按下时的坐标和鼠标左键抬起时的坐标,然后把它们分别给Element 0和Element 1,那这条线不就是画出来了吗?

那我们就开始写脚本咯:

using UnityEngine;
using System.Collections;

public class tes : MonoBehaviour 

    Vector3 pos1;     //定义起点坐标
    Vector3 pos2;     //定义终点坐标

    LineRenderer myLinRenderer;     //定义一个LineRenderer对象

    // Use this for initialization
    void Start () 
        myLinRenderer = GetComponent<LineRenderer>();     //给LineRenderer对象初始化
    

    // Update is called once per frame
    void Update () 
        if (Input.GetMouseButtonDown(0))      //当鼠标左键按下的时候
        
            pos1 = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y,1));     //将起点坐标记录下来
        

        if (Input.GetMouseButtonUp(0)) 
        
            pos2 = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,1));     //将终点坐标记录下来
            myLinRenderer.SetPosition(0,pos1);
            myLinRenderer.SetPosition(1,pos2);     //分别将起始坐标和终点坐标付给相应的变量
        
    

写好之后我们先来想一下,LinRenderer是给它设置坐标它就开始渲染了,那上面我们写好的脚本也就是说:当我鼠标松开的时候,这条线才能画出来,而当鼠标一直按下的时候,这条线我们是看不到的。

我们把这个脚本给我们创建的游戏体Knife,运行后:

我们可以看到,在Scenes面板中确实有一条类似刀痕的东西,但是有两个问题:
颜色和我们的好像不太一样,那是因为在这里:

有Parameters属性,这里可以设置刀片的起始宽度和终点宽度,起始颜色到终点颜色的过渡,默认是白色的。

另一个严重的问题是:在Game面板中根本不显示这条刀痕,左侧的Scenes面板中明明是能显示的!
我们看运行的时候,BackGround的位置是(0.5,0.5,0),摄像机的位置是(0,1,-10),而且是正交摄像机:

此时刀片的两个坐标的Z都是-9,说明不是脚本的问题:

按理来说刀片是完全能显示的,我们再检查一下层Layer,即使重新设置了Layer也还是无法显示。

好像有点懵逼了。。。

经过一系列的检查,终于找到了症结所在,从一开始我们就错了,还记得一开始我们说那个BackGround在Scenes面板中看不到对吧?因为我们添加的是GUITexture,这里我们做错了,不应该添加GUITexture组件,而应该是SpriteRenderer:

这下我们在Scenes面板和Game面板下都可以看到了:

Game下显示不对,我们把BackGround的Scale调整为(2.4,2.4,1):

然后我们把BackGround和Knife的Layer都改为默认,(因为大家可能发现截图中他们的Layer是NGUI,那是因为我在做后面的一种方法的时候没有改过来。),同时,摄像机的CullingMask保持默认的Everything。
我们再来运行一下:
鼠标按下,并移动一段距离,没有反应:

当松开鼠标的时候:

啊哈哈哈哈!
终于粗来了!

而且好帅气的对不对?

没错,就是这样。

之前发现了问题,为什么我没有把之前的内容修改掉,而是在文章中用不少的篇幅来找错误呢?那是因为我觉得我们所犯的每一个错误对我们自身而言都十分有意义,当我有一天回头看看这篇文章的时候,我会对GUITexture和SpriteRenderer有十分深刻的印象,就酱~

言归正传:
但是,我们又发现了一个问题:这里面的刀痕只有一条,那我现在所画的刀痕不是直线,而是有拐点的呢?它还是只是显示一条连接起始点和终点的刀痕,中间的拐点全都没有表现出来。

既然这样,那么我们把中间的拐点也都记录下来不就好了吗?

我们这样来思考:当我鼠标左键按下的时候,每隔0.2秒记录一次鼠标的位置,然后把它加到那个位置坐标数组中:
其实这个位置坐标更像一个泛型,因为它有数组的性质,但是又没有长度限制:

我们可以这样,每隔0.2秒,就让这个泛型的长度加1,然后把坐标填进去,只要设置了两个或两个以上坐标,这条线就能画得出来:

修改代码如下:

using UnityEngine;
using System.Collections;

public class tes : MonoBehaviour 

    Vector3 pos1;
    Vector3 pos2;

    LineRenderer myLinRenderer;

    int count=0;    //定义一个变量,用来修改泛型的长度

    float time = 0.2f;  //定义一个时间间隔
    // Use this for initialization
    void Start () 
        myLinRenderer = GetComponent<LineRenderer>();
    

    // Update is called once per frame
    void Update () 
        if (Input.GetMouseButtonDown(0)) 
        
            pos1 = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y,1));
            myLinRenderer.SetVertexCount(1);    //第一次按下鼠标左键时,应该有了一个点,所以把节点的数目设置为1
            myLinRenderer.SetPosition(0, pos1);     //有了一个节点就设置一次坐标,而不是单单记录坐标
        

        if (Input.GetMouseButton(0))    //当鼠标一直按下的时候
        
            time -= Time.deltaTime;     //时间进行递减
            if (time <= 0)      //当时间减小到0或0以下时,
            
                time = 0.2f;        //把时间重置
                count++;        //节点数自增

                Vector3 pos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 1));     //纪录此时的坐标值
                myLinRenderer.SetVertexCount(count);        //每隔0.2秒,就增加一个节点,增加一个点的方法就是SetVertexCount()方法
                count--;        //count还要自减,因为在设置位置的时候,索引是比节点数小1的
                myLinRenderer.SetPosition(count,pos);       //设置这个增加的节点的位置
                count++;        //最后,因为增加了一个节点,所以count是要增加1的

            
        

        if (Input.GetMouseButtonUp(0))      //当鼠标左键抬起的时候,依旧要纪录最后一个点的坐标,最终把count重置为0
        
            count++;
            pos2 = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,1));
            myLinRenderer.SetVertexCount(count);
            count--;
            myLinRenderer.SetPosition(count,pos2);
            count = 0;
        
    

代码中加了注释的地方就是修改的地方,对于count的值得问题,我用的是这种笨方法,需要思考一下才能理清。

保存运行,就得到了:

效果已经出来了,然而,LineRenderer在拐弯的地方自己处理不好,有时候就会变成这样:

这是因为拐弯的时候涉及到了宽度的变化,宽度变化就会出现这种情况。

我们把刀片的Parameters属性下的Start width和Endwidth的值设置不同,比如一个为1,一个为3:
在把第一个脚本挂上来,运行一下:

我们会发现,怎么都不是一条直线了,因为起始宽度是1,终点宽度是3,1到3不是自然过渡的,所以就好像是在中间突然少了一块一样,这是LineRenderer的问题,同理,我们在转弯的时候遇到的问题与它类似。

不过可以适当调小它们的宽度来使其更自然一些,比如起始和终点宽度都设为0.3:

这样就稍微好一点,但是解决不了根本问题,要彻底解决需要自己写脚本来控制。

这里我们就把第一种方法给讲完了,接下来说第二种方法:在NGUI中实现鼠标画刀痕:
Ctrl+N新建一个Scenes,命名随意,导入NGUI的包。
找到NGUI->Open->Prefab Toolbar,


把左上角的背景拖入Hierarchy面板中:

找到BG - Stripes,把它删掉:

依次打开:

点击Create,选择目录并命名后,点击保存,不要关闭这个窗口,在Project面板中找到背景图片,我们会发现Create按钮变成了Add/Create,点击它。这张背景图片后面出现了Update字样:

现在可以关闭这个窗口了。

选中Hierarchy中的Control - Background,在右侧的Inspector面板中找到UISprite,点击Atlas:

弹窗如下窗口:

找到你刚才命名的那个prefab,如果没有,就点击下面的Show All,然后点击对应后面的Select,然后在点击Sprite:

在弹出的面板中选择背景图片,并双击它。

我们发现一片漆黑:
点击右面的ColorTint(点击它右侧的黑色区域):

弹出如下窗口:

用鼠标将方框左下角的白色圆圈移到左上方的白色区域:

结果就能正常显示了:

上面都是一些废话,基本上大家都会操作的。

接下来按照上面相同的方式创建刀片,并把上一个脚本拖给它(做一点点修改):

using UnityEngine;
using System.Collections;

public class tes : MonoBehaviour 

    Vector3 pos1;
    Vector3 pos2;

    LineRenderer myLinRenderer;

    int count=0;   

    float time = 0.2f;  

    public Camera myCamera;     //声明一个公开的摄像机类型(天哪,什么类型都有。。。)
    // Use this for initialization
    void Start () 
        myLinRenderer = GetComponent<LineRenderer>();
    

    // Update is called once per frame
    void Update () 
        if (Input.GetMouseButtonDown(0)) 
        
            pos1 = myCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -1));     //这里要做修改
            myLinRenderer.SetVertexCount(1);    
            myLinRenderer.SetPosition(0, pos1);     

        

        if (Input.GetMouseButton(0))    
        
            time -= Time.deltaTime;    
            if (time <= 0)      
            
                time = 0.2f;        
                count++;        

                Vector3 pos = myCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -1));       //这里要做修改   
                myLinRenderer.SetVertexCount(count);        
                count--;        
                myLinRenderer.SetPosition(count, pos);       
                count++;        

            
        

        if (Input.GetMouseButtonUp(0))      
        
            count++;
            pos2 = myCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -1));     //这里要做修改
            myLinRenderer.SetVertexCount(count);
            count--;
            myLinRenderer.SetPosition(count,pos2);
            count = 0;
        
    

加注释的地方就是修改的地方,把所有Camera.main.ScreenToWorldPoint全部改为myCamera.ScreenToWorldPoint(myCamera要看你的具体命名。)
还有后面的Z轴值也要改为-1.

Ctrl+S保存脚本,回到Unity中,选中刀片,会看到多了一个myCamera:

把左侧的Camera沿着箭头拖过去:

运行:

宽度自己调一下吧,如果不显示,设置一下Knife的Layer和Camera的Culling Mask就好了。

只需要修改一点点的脚本就能运用在NGUI中了。

嗯,这就是我个人目前实现切水果刀痕的两种方式,如果大家有什么好的办法欢迎交流哦~

同时,文章里有什么有误的地方,欢迎大家批评指正!谢谢!

以上是关于Unity知识三:两种方式实现切水果的刀痕的主要内容,如果未能解决你的问题,请参考以下文章

基于HTML5和JS实现的切水果游戏

百科知识 什么是陶瓷水果刀

Pygame实战风靡全球的切水果游戏升级版“水果忍者”上线啦!你敢来PK嘛?!

置顶篇——欢迎来到我的博客

Unity实现绘制线断一 ————利用LineRenderer组件划线的两种方式

codevs 1299 切水果 线段树