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 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#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更惬意一点,休闲为主,不搞太累。