C for Graphic:DNF手游残影效果

news/2024/10/7 15:16:59/

      dnf手游在作死的道路上越行越远,困难罗特斯完全打不动,提前在抖音上细看攻略,基本能躲过机制不死,但是伤害不够,全时打满也还剩3000+管血,组团半天+炸团半天=完全浪费一天。
     个人觉得策划完全没必要这么逼氪,毕竟才开服四个月,就马不停蹄的想逼玩家氪金,可见游戏后续发展基本玩不下去,最后记录一下用得上的功能就退游。
      dnf手游操作里,按两下后跳,就会朝着操作方向快速闪现,同时出现滞留残影,有时候会发生一个残影一直滞留的bug,满屏幕到处跑会产生满屏幕残影,可见dnf开发人员加班挺凶的,很多bug估计来不及修。
      制作角色残影的核心就是连续记录角色上一段时间的状态,至于具体怎么记录角色状态,依据不同项目的情况而定。
      比如在3d项目中,角色状态就是通过bakemesh出不同时间的instancedmesh,再对instancedmesh进行渲染操作。
      而2d项目中,用spriterender制作一个2d角色,如下:
在这里插入图片描述
      记录2d角色状态,就是记录不同时间spriterender的sprite。
      但是这次我想用后处理完成这个功能。
      核心:按时间间隔采样mesh并使用commandbuffer渲染,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using System.Linq;
using System;public class ESAfterEffect : MonoSingleton<ESAfterEffect>
{public class SpriteParam{public Mesh Ms;public Vector3 WorldPos;public Quaternion WorldQua;public Vector3 WorldScale;public Material Mat;public float Countdown;public float Elapse;}private CommandBuffer cmdBuffer;private const int SPRITE_COUNT = 10;private List<SpriteParam> sprList = new List<SpriteParam>();void Start(){}void Update(){DestroyBuffer();if (sprList.Count > 0){for (int i = 0; i < sprList.Count; i++){SpriteParam spr = sprList[i];if (cmdBuffer == null){cmdBuffer = new CommandBuffer();Camera.main.AddCommandBuffer(CameraEvent.AfterEverything, cmdBuffer);}cmdBuffer.DrawMesh(spr.Ms, Matrix4x4.TRS(spr.WorldPos, spr.WorldQua, spr.WorldScale), spr.Mat);spr.Elapse -= Time.deltaTime;float alpha = spr.Elapse / spr.Countdown;spr.Mat.SetFloat("_Alpha", alpha);if (spr.Elapse < 0f){sprList.Remove(spr);}}}}private void OnRenderImage(RenderTexture source, RenderTexture destination){if (cmdBuffer != null){Graphics.ExecuteCommandBuffer(cmdBuffer);}Graphics.Blit(source, destination);}public void DrawMesh(Mesh ms, Vector3 wpos, Quaternion wqua, Vector3 wsca, Material mat, float cd = 0.3f){SpriteParam pam = new SpriteParam{Ms = ms,WorldPos = wpos,WorldQua = wqua,WorldScale = wsca,Mat = mat,Countdown = cd,Elapse = cd,};DrawMesh(pam);}public void DrawMesh(SpriteParam spr){if (sprList.Count > SPRITE_COUNT){sprList.RemoveTail();}sprList.Add(spr);}public void DestroyBuffer(){if (cmdBuffer != null){cmdBuffer.Clear();cmdBuffer = null;}}}

      调用绘制

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;public class ESCharacterGhost : MonoBehaviour
{public SpriteRenderer spriteRd;public RectTransform rectRd;public Material ghostMat;public float sampleInterval = 0.1f;private float sampleElapse = 0f;private Vector2 lastAnchorPos;void Start(){}void Update(){sampleElapse += Time.deltaTime;if (sampleElapse > sampleInterval){if (IsPositionChanged()){lastAnchorPos = rectRd.anchoredPosition;Sprite spr = spriteRd.sprite;Material mat = new Material(ghostMat);mat.SetTexture("_MainTex", spr.texture);mat.SetInt("_Inverse", spriteRd.flipX ? 1 : 0);ESAfterEffect.Instance.DrawMesh(GetSpriteMesh(spr), transform.position, transform.rotation, transform.lossyScale, mat);sampleElapse = 0f;}}}private bool IsPositionChanged(){if (MathModule.Instance.CheckVector2Approximate(lastAnchorPos, rectRd.anchoredPosition)){return false;}return true;}private Mesh GetSpriteMesh(Sprite spr){Mesh mesh = new Mesh();mesh.SetVertices(Array.ConvertAll(spr.vertices, x => (Vector3)x).ToList());mesh.SetUVs(0, spr.uv.ToList());mesh.SetTriangles(spr.triangles, 0);return mesh;}
}

      渐变着色

Shader "EchoShadow/EchoShadowAlphaShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Inverse("Inverse X",int) = 0_Alpha("Alpha",Range(0, 1)) = 1}SubShader{Tags { "RenderType"="Transparent" "Queue"="Transparent" }LOD 100Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;int _Inverse;float _Alpha;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{if(_Inverse == 1){i.uv.x = (1-i.uv.x);}fixed4 col = tex2D(_MainTex, i.uv);col.a *= _Alpha;return col;}ENDCG}}
}

      最终效果如下:
在这里插入图片描述
      重回dnf端游pk更惬意一点,休闲为主,不搞太累。


http://www.ppmy.cn/news/1535752.html

相关文章

qsort函数及其使用的方法分解讲解

qsort函数 默认排序升序 void qsort(void* base,//指向待排序数组的第一个元素的地址 size_t num,//base指向数组中元素的个数 size_t size,//base指向的数组中一个元素的大小&#xff0c;单位是字节 int (*compar)(const void*,const void*…

vue3打包疯狂报错

打包的时候报错很多Cannot find name ‘xxx‘ 。 但是npm run dev 是运行正常的。 解决方法&#xff1a;package.json中的vue-tsc --noEmit 删掉就可以了。 例如&#xff1a; 这是原来的 {"scripts": {"dev": "vite","build": &quo…

Allen Institute for Artificial Intelligence (Ai2) 发布开源多模态语言模型 Molmo

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

C++常用数据结构

1: vector使用示例 #include <iostream> #include <vector> #include <algorithm> using namespace std;int main() {// 初始化vector<int> a;vector<int> b(5); // 会初始化每个元素的值为0vector<int> c(6, 2);vector<int> d {1…

yolov11 部署瑞芯微rk3588、RKNN部署工程难度小、模型推理速度快

yolov8还没玩溜&#xff0c;yolov11又来了&#xff0c;那么部署也又来了。 特别说明&#xff1a;如有侵权告知删除&#xff0c;谢谢。 完整代码&#xff1a;包括onnx转rknn和测试代码、rknn板端部署C代码 【onnx转rknn和测试代码】 【rknn板端部署C代码】 1 模型训练 yolov1…

Acwing 数位统计DP

Acwing 338.计数问题 输入样例&#xff1a; 1 10 44 497 346 542 1199 1748 1496 1403 1004 503 1714 190 1317 854 1976 494 1001 1960 0 0 输出样例&#xff1a; 1 2 1 1 1 1 1 1 1 1 85 185 185 185 190 96 96 96 95 93 40 40 40 93 136 82 40 40 40 40 115 666 215 215 214…

系统设计,如何设计一个秒杀功能

需要解决的问题 瞬时流量的承接防止超卖预防黑产避免对正常服务的影响兜底方法 前端设计 利用 CDN 缓存静态资源&#xff0c;减轻服务器的压力在前端随机限流按钮防抖&#xff0c;防止用户重复点击 后端设计 Nginx 做统一接入&#xff0c;进行负载均衡与限流用 sentinel 等…

Android15车载音频之Virtualbox中QACT实时调试(八十八)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+…