unity 使用像素实现墙面子弹留孔效果(给已有贴图模型叠加贴图)

在很多枪战游戏中,都有子弹射击到物体上后出现弹孔的效果,类似的贴图功能早已实现。由于项目需要,最近接触到这一需求,需要在场景模型中特定的地方做标记,本来一开始想着用贴Plane面片的方式实现,也就是在射线射中的模型对应的点生成一张带贴图的面片,但随后发现,这种方式只适合平面,若在弯曲的地方,面片无法贴合在模型上,于是就只能通过修改模型贴图的方式,也就是说在模型的贴图上在叠加弹孔,这样的话,子弹孔就和模型融为一体,就可适用于曲面了。网上已有相关的实现方法,我做了些修改和添加优化,方便有类似模型叠加贴图需求的快速实现。

一需要涉及的东西:

1墙面:我用球来代替,这样可以通过球的曲面直观看出贴图效果。
 2墙面贴图:需要对它进行编辑。
 3弹孔贴图:用来贴到墙面贴图上。

二实现过程:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletHit : MonoBehaviour
{
    // 原来的墙壁纹理
    public Texture2D m_oldwalltexture;

    // 新创建一个墙壁的纹理图片

    Texture2D m_newwalltexture;

    // 子弹的纹理图片
    public Texture2D[] m_bullettextures;
    private Texture2D m_bullettexture;



    float m_wallwidth;

    float m_wallheight;

    float m_bulletwidth;

    float m_bulletheight;

    float m_timer;

    //鼠标点击位置得到的Uv坐标 使用队里存储

    Queue UVque = new Queue();

    void Awake()

    {

        // 原来的墙壁纹理

        m_oldwalltexture = GetComponent<MeshRenderer>().materials[0].mainTexture as Texture2D;

        // 为了以后修改的纹理以后还能还原,我使用一个备份的纹理

        m_newwalltexture = Instantiate(m_oldwalltexture);

        //现在使用备份的纹理图片,这样就算修改也修改是备份的图片

        GetComponent<MeshRenderer>().materials[0].mainTexture = m_newwalltexture;

        // 拿到墙壁和子弹的纹理宽度和高度

        m_wallwidth = m_newwalltexture.width;

        m_wallheight = m_newwalltexture.height;

    }

    public void SetBulletRect(int no, int width, int height)
    {
        m_bullettexture = ScaleTexture(m_bullettextures[no],width,height);
        m_bulletwidth = m_bullettexture.width;

        m_bulletheight = m_bullettexture.height;
    }


    void Update()

    {

        // 鼠标位置--射线
        
        //鼠标左键实现效果1贴图
        if (Input.GetMouseButtonDown(0))
        {
            SetBulletRect(0,32,32);
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.gameObject!=null)
                {
                    StartCoroutine(DrawPixel(hit));
                }

            }

        }


        //鼠标右键实现效果2贴图
        if (Input.GetMouseButtonDown(1))
        {
            SetBulletRect(1, 32, 64);
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.gameObject != null)
                {
                    StartCoroutine(DrawPixel(hit));
                }

            }

        }

    }
    

    IEnumerator DrawPixel(RaycastHit hit)
    {
        Vector2 uv = hit.textureCoord;


        UVque.Enqueue(uv);

        for (int i = 0; i < m_bulletwidth; i++)
        {

            for (int j = 0; j < m_bulletheight; j++)
            {

                float w = (uv.x * m_wallwidth - m_bulletwidth / 2) + i;

                float h = (uv.y * m_wallheight - m_bulletheight / 2) + j;

                Color bulletPixels = m_bullettexture.GetPixel(i, j);


                Color wallpixsls = m_newwalltexture.GetPixel((int)w, (int)h);

                //若透明通道则像素不变,使用墙面原像素,否则使用弹孔像素
                if (bulletPixels.a == 0)
                {
                    m_newwalltexture.SetPixel((int)w, (int)h, wallpixsls);
                }
                else
                {
                    m_newwalltexture.SetPixel((int)w, (int)h, bulletPixels);
                }

            }

        }

        m_newwalltexture.Apply();
        yield return m_newwalltexture;
    }

    Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
    {
        Texture2D result = new Texture2D(targetWidth, targetHeight, source.format, false);

        float incX = (1.0f / (float)targetWidth);
        float incY = (1.0f / (float)targetHeight);

        for (int i = 0; i < result.height; ++i)
        {
            for (int j = 0; j < result.width; ++j)
            {
                Color newColor = source.GetPixelBilinear((float)j / (float)result.width, (float)i / (float)result.height);
                result.SetPixel(j, i, newColor);
            }
        }

        result.Apply();
        return result;
    }

}

实现逻辑比较简单,主要就是通过射线找到墙面贴图上textureCoord,也就是附着点的UV坐标,然后在将弹孔贴图的像素点替换到墙面贴图上对应的点,由于弹孔图一般来说不很大,因此并不怎么耗废内存。

其中DrawPixel()方法用来绘制。

ScaleTexture用来根据需要缩放弹孔贴图。

效果如下:

墙面:

贴图后墙面:

附百度网盘下载地址:

链接: https://pan.baidu.com/s/1N7k4aZ029Aw6llIoMr_RDA 提取码: pc8i

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值