unity 有打击感的图片,怎么做动画,可以表现出良好的打击效果

 完整实现脚本:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

[RequireComponent(typeof(Image))]
public class HitEffectController : MonoBehaviour
{
    [Header("基础设置")]
    public float hitDuration = 0.5f; // 打击效果总时长
    
    [Header("缩放效果")]
    public float maxScale = 1.3f; // 最大缩放值
    public float scaleInDuration = 0.05f; // 缩放进入时间
    public float scaleOutDuration = 0.2f; // 缩放恢复时间
    public AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
    
    [Header("震动效果")]
    public float shakeIntensity = 10f; // 震动强度
    public float shakeFrequency = 25f; // 震动频率
    public AnimationCurve shakeCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
    
    [Header("闪光效果")]
    public Color flashColor = Color.white; // 闪光颜色
    public float flashDuration = 0.1f; // 闪光持续时间
    public float flashIntensity = 5f; // 闪光强度
    
    [Header("运动模糊")]
    public bool useMotionBlur = true;
    public float stretchAmount = 0.3f; // 拉伸量
    public float stretchDuration = 0.08f; // 拉伸持续时间
    
    [Header("粒子效果")]
    public ParticleSystem hitParticles;
    public int particleCount = 15; // 粒子数量
    
    [Header("时间效果")]
    public float freezeDuration = 0.05f; // 时间冻结时长
    
    private Image targetImage;
    private RectTransform rectTransform;
    private Vector3 originalScale;
    private Color originalColor;
    private Material originalMaterial;
    private Coroutine currentEffect;
    
    void Awake()
    {
        targetImage = GetComponent<Image>();
        rectTransform = GetComponent<RectTransform>();
        originalScale = rectTransform.localScale;
        originalColor = targetImage.color;
        originalMaterial = targetImage.material;
    }
    
    // 触发打击效果
    public void TriggerHitEffect()
    {
        if (currentEffect != null) StopCoroutine(currentEffect);
        currentEffect = StartCoroutine(HitEffectRoutine());
    }
    
    private IEnumerator HitEffectRoutine()
    {
        // 1. 时间冻结(强调击中瞬间)
        Time.timeScale = 0f;
        yield return new WaitForSecondsRealtime(freezeDuration);
        Time.timeScale = 1f;
        
        // 2. 初始缩放(表现冲击)
        float scaleTimer = 0f;
        while (scaleTimer < scaleInDuration)
        {
            scaleTimer += Time.deltaTime;
            float t = scaleCurve.Evaluate(scaleTimer / scaleInDuration);
            float scale = Mathf.Lerp(1f, maxScale, t);
            rectTransform.localScale = originalScale * scale;
            yield return null;
        }
        
        // 3. 同时触发闪光和粒子效果
        StartCoroutine(FlashEffect());
        EmitParticles();
        
        // 4. 运动模糊(可选)
        if (useMotionBlur)
        {
            StartCoroutine(StretchEffect());
        }
        
        // 5. 缩放恢复+震动效果
        float shakeTimer = 0f;
        Vector3 originalPosition = rectTransform.localPosition;
        
        while (shakeTimer < hitDuration)
        {
            shakeTimer += Time.deltaTime;
            
            // 缩放恢复
            if (shakeTimer < scaleOutDuration)
            {
                float scaleT = scaleCurve.Evaluate(shakeTimer / scaleOutDuration);
                float scale = Mathf.Lerp(maxScale, 1f, scaleT);
                rectTransform.localScale = originalScale * scale;
            }
            
            // 震动效果
            float shakeT = shakeCurve.Evaluate(shakeTimer / hitDuration);
            float shakeMagnitude = shakeIntensity * (1f - shakeT);
            
            float offsetX = Mathf.Sin(Time.time * shakeFrequency) * shakeMagnitude;
            float offsetY = Mathf.Cos(Time.time * shakeFrequency) * shakeMagnitude;
            rectTransform.localPosition = originalPosition + new Vector3(offsetX, offsetY, 0);
            
            yield return null;
        }
        
        // 恢复原始状态
        rectTransform.localScale = originalScale;
        rectTransform.localPosition = originalPosition;
    }
    
    // 闪光效果协程
    private IEnumerator FlashEffect()
    {
        // 创建临时材质实现闪光
        Material flashMaterial = new Material(Shader.Find("UI/Unlit/Transparent"));
        targetImage.material = flashMaterial;
        
        float timer = 0f;
        while (timer < flashDuration)
        {
            timer += Time.deltaTime;
            float t = Mathf.Clamp01(timer / flashDuration);
            
            // 强度曲线:快速达到峰值然后衰减
            float intensity = flashIntensity * Mathf.Sin(t * Mathf.PI);
            
            // 混合原始颜色和闪光颜色
            Color blendedColor = Color.Lerp(originalColor, flashColor, intensity);
            flashMaterial.color = blendedColor;
            
            yield return null;
        }
        
        // 恢复原始材质
        targetImage.material = originalMaterial;
        Destroy(flashMaterial);
    }
    
    // 运动模糊拉伸效果
    private IEnumerator StretchEffect()
    {
        Vector3 originalScale = rectTransform.localScale;
        Vector3 stretchedScale = new Vector3(
            originalScale.x * (1f + stretchAmount),
            originalScale.y * (1f - stretchAmount * 0.5f),
            originalScale.z
        );
        
        float timer = 0f;
        while (timer < stretchDuration)
        {
            timer += Time.deltaTime;
            float t = timer / stretchDuration;
            
            // 使用缓动函数实现快速拉伸然后恢复
            float stretchT = Mathf.Sin(t * Mathf.PI * 0.5f);
            rectTransform.localScale = Vector3.Lerp(originalScale, stretchedScale, stretchT);
            
            yield return null;
        }
        
        rectTransform.localScale = originalScale;
    }
    
    // 发射粒子
    private void EmitParticles()
    {
        if (hitParticles != null)
        {
            // 在图片位置发射粒子
            hitParticles.transform.position = rectTransform.position;
            hitParticles.Emit(particleCount);
        }
    }
}

 


 

参数优化建议
缩放效果
csharp
maxScale = 1.2f - 1.5f; // 根据图片大小调整
scaleInDuration = 0.03f - 0.07f; // 快速冲击
scaleOutDuration = 0.15f - 0.3f; // 缓慢恢复
震动效果
csharp
shakeIntensity = 5f - 20f; // 小图片用小值,大图片用大值
shakeFrequency = 20f - 30f; // 高频震动更真实
闪光效果
csharp
// 根据图片主色调选择对比色
flashColor = ComplementaryColor(originalColor); 
flashIntensity = 3f - 8f; // 强度根据场景亮度调整

高级效果增强技巧

1. 材质与着色器效果

冲击波着色器:

// 简化的冲击波着色器
Shader "Custom/HitWave"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _WaveCenter ("Wave Center", Vector) = (0.5, 0.5, 0, 0)
        _WaveAmount ("Wave Amount", Range(0, 1)) = 0
        _WaveIntensity ("Wave Intensity", Float) = 10
    }
    
    SubShader
    {
        // 着色器代码...
        
        float2 waveOffset = (distance(i.uv, _WaveCenter.xy) * _WaveIntensity * _WaveAmount;
        float2 distortedUV = i.uv + waveOffset;
        fixed4 col = tex2D(_MainTex, distortedUV);
        
        // 增加边缘发光
        col.rgb += _WaveAmount * _WaveIntensity * 0.1 * (1 - smoothstep(0, 0.2, distance(i.uv, _WaveCenter.xy)));
    }
}

2. 屏幕空间效果

全局屏幕震动:

public class ScreenShake : MonoBehaviour
{
    public float duration = 0.5f;
    public float intensity = 0.1f;
    
    private Vector3 originalPos;
    private float currentShakeTime;
    
    void Start()
    {
        originalPos = transform.localPosition;
    }
    
    public void Shake()
    {
        currentShakeTime = duration;
    }
    
    void Update()
    {
        if (currentShakeTime > 0)
        {
            float percent = currentShakeTime / duration;
            Vector2 offset = UnityEngine.Random.insideUnitCircle * intensity * percent;
            transform.localPosition = originalPos + new Vector3(offset.x, offset.y, 0);
            currentShakeTime -= Time.deltaTime;
        }
        else
        {
            transform.localPosition = originalPos;
        }
    }
}

  3. 音效同步

[Header("音效设置")]
public AudioClip hitSound;
public AudioClip impactSound;
public float soundDelay = 0.02f;

private AudioSource audioSource;

void Start()
{
    audioSource = gameObject.AddComponent<AudioSource>();
}

private IEnumerator PlayHitSounds()
{
    audioSource.PlayOneShot(hitSound);
    yield return new WaitForSeconds(soundDelay);
    audioSource.PlayOneShot(impactSound);
}

 

 4. 物理模拟碎片

[Header("碎片效果")]
public GameObject fragmentPrefab;
public int fragmentCount = 10;
public float explosionForce = 300f;

private void CreateFragments()
{
    for (int i = 0; i < fragmentCount; i++)
    {
        GameObject fragment = Instantiate(fragmentPrefab, transform.position, Quaternion.identity);
        Rigidbody2D rb = fragment.GetComponent<Rigidbody2D>();
        
        if (rb != null)
        {
            Vector2 direction = Random.insideUnitCircle.normalized;
            rb.AddForce(direction * explosionForce);
            rb.AddTorque(Random.Range(-100f, 100f));
        }
        
        Destroy(fragment, Random.Range(0.5f, 1.5f));
    }
}

 参数组合方案

参数组合方案
轻度打击效果
csharp
hitDuration = 0.3f;
maxScale = 1.15f;
shakeIntensity = 5f;
flashIntensity = 3f;
freezeDuration = 0.02f;
中度打击效果
csharp
hitDuration = 0.5f;
maxScale = 1.25f;
shakeIntensity = 10f;
flashIntensity = 5f;
freezeDuration = 0.05f;
useMotionBlur = true;
重度打击效果
csharp
hitDuration = 0.7f;
maxScale = 1.4f;
shakeIntensity = 15f;
flashIntensity = 8f;
freezeDuration = 0.08f;
useMotionBlur = true;
// 启用碎片效果
CreateFragments();

 


 其他:

性能优化建议
对象池管理:

csharp
public class FragmentPool : MonoBehaviour
{
    public GameObject fragmentPrefab;
    public int poolSize = 20;
    
    private Queue<GameObject> fragmentPool = new Queue<GameObject>();
    
    void Start()
    {
        for (int i = 0; i < poolSize; i++)
        {
            GameObject fragment = Instantiate(fragmentPrefab);
            fragment.SetActive(false);
            fragmentPool.Enqueue(fragment);
        }
    }
    
    public GameObject GetFragment()
    {
        if (fragmentPool.Count > 0)
        {
            GameObject fragment = fragmentPool.Dequeue();
            fragment.SetActive(true);
            return fragment;
        }
        return Instantiate(fragmentPrefab);
    }
    
    public void ReturnFragment(GameObject fragment)
    {
        fragment.SetActive(false);
        fragmentPool.Enqueue(fragment);
    }
}
材质共享:

csharp
private static Material flashMaterial;

void Awake()
{
    if (flashMaterial == null)
    {
        flashMaterial = new Material(Shader.Find("UI/Unlit/Transparent"));
    }
    // 使用共享材质
}
按需启用:

csharp
void OnEnable()
{
    // 初始化
}

void OnDisable()
{
    // 清理资源
}
使用示例
csharp
// 在UI按钮上添加点击效果
public class HitButton : MonoBehaviour
{
    public HitEffectController hitEffect;
    
    void Start()
    {
        Button button = GetComponent<Button>();
        button.onClick.AddListener(OnButtonClick);
    }
    
    void OnButtonClick()
    {
        hitEffect.TriggerHitEffect();
    }
}

// 在游戏角色受击时
public class CharacterHealth : MonoBehaviour
{
    public HitEffectController hitEffect;
    public ScreenShake screenShake;
    
    public void TakeDamage()
    {
        hitEffect.TriggerHitEffect();
        screenShake.Shake();
        
        // 其他伤害处理...
    }
}
效果组合策略
轻重攻击区分:

轻攻击:缩放+轻微震动

重攻击:缩放+震动+闪光+屏幕震动+粒子

元素类型反馈:

物理攻击:灰色粒子+屏幕震动

火焰攻击:红色闪光+火焰粒子

冰霜攻击:蓝色闪光+冰晶粒子

命中部位差异:

头部命中:加强屏幕震动

身体命中:标准效果

四肢命中:减弱效果

总结
要创建具有强烈打击感的图片动画,关键在于多种效果的组合运用:

时间控制:通过时间冻结强调击中瞬间

空间变形:缩放、拉伸和震动表现物理冲击

视觉特效:闪光和粒子增加细节反馈

辅助效果:屏幕震动和音效增强沉浸感

参数优化:根据不同场景调整效果强度

通过调整上述脚本中的参数和组合不同的效果模块,可以为各种游戏场景创建出令人满意的打击效果。记得在实际应用中根据游戏风格和性能要求进行适当调整。

 


 转载于:

### Unity中实现攻击动画打断的方法 在Unity中,为了实现在角色播放攻击动画期间能够因受到攻击而中断当前动画并触发新的受伤动画,可以通过配置Animator控制器中的状态转换条件以及合理利用`Has Exit Time`选项来达成这一目标[^1]。 对于希望立即停止现有动画并启动新动画的情况,在两个不同的受伤状态下交替切换是一种有效策略。每当检测到一次被击事件发生时,通过判断累计的打击次数是奇数还是偶数来决定进入哪一个具体的受伤状态;与此同时,确保所有涉及快速响应的状态间迁移都不启用`Has Exit Time`特性,从而保障动作间的无缝衔接。 另外一种更为直接的方式是在遭遇外部干扰(比如敌人攻击)的时候调用`Animator.Play("None")`命令强制跳转至空闲态或者不存在实际视觉表现的一个特殊节点上,这样做不仅可以让正在进行的动作瞬间终止,而且还能让那些依赖于特定姿态下的子对象迅速复位回到初始设定之中[^2]。 #### 示例代码展示如何监听碰撞消息并改变动画: ```csharp private Animator anim; void Start(){ anim = GetComponent<Animator>(); } // 假设此函数由物理引擎或其他逻辑部分调用来表示受到了伤害 public void OnHit(int hitCount){ if(hitCount % 2 == 0){ // 如果hitCount为偶数,则播放Hurt2动画 anim.SetInteger("HitState", 2); }else{ // 否则播放Hurt1动画 anim.SetInteger("HitState", 1); } } // 或者使用更激进的方式来处理 public void ImmediateInterruptByAttack(){ anim.Play("None"); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值