这几天做了个小游戏,给家里人玩玩,其中很多东西需要自己去弄:

  • 程序 : CSharp、ShaderLab
  • 动画 : DOTween(没去用 Unity 的 Animation 或是 Timeline)
  • 美术 : 配色,部分纹理
  • UI : 使用 UGUI(不熟悉,都是临时查阅资料就能用)
  • AI : 因为游戏比较简单,AI 也没用行为树,直接配置控制甩武器的速度参数即可即可
  • 特效 : Unity 粒子系统(不熟悉,都是临时查阅资料就能用)
  • 音效 : 自己网上找免费资源
  • 模型 : Unity 内置 Cube,Sphere,Cylinder 足够了,要是对外发布,可能还要自己学习建模 Blender(有计划,现在还没开始,惭愧)

使用 DOTween 来实现的小游戏部分动画内容

封面

在这里插入图片描述

封面继续

在这里插入图片描述

菜单项

在这里插入图片描述
在这里插入图片描述

进入对战场景

这里就不演示对战内容了(也特效,AI,镜头效果,结算,等)
还可以实现一些出场动画,等,其实这个小游戏项目可以完善的内容挺多的
在这里插入图片描述

最后是战斗中返回开始界面
在这里插入图片描述

总体来说,DOTween 虽然用起来不够顺手,但是也够用,相比,iTween,LeanTween 都好用一些。

部分动画我本想用 Unity Animation 来实现的,但是反而觉得可控制不太友好,所以就使用 Tween 库来封装一些类来处理,也是比较灵活的,如果是公司项目要给策划用的编辑器就不一样了,项目的接口设计会完全不一样。

这个小游戏项目从无到有,完全没用到以前积累的东西,也是蛋疼。

也是对 Unity 不够熟悉。

代码写得非常的简单易懂,基本没封装什么东西,就封装了一个 Loading 场景的统一处理,其他都是乱七八糟的代码。。。-_-!!!

后面有空需要去参考一些 Unity 现成的一些框架,参考他们的设计,再做适合自己项目的封装。

不然以后使用起来制作就相当麻烦了。


在使用一些动画的时候,找了一些 Tween 缓存库。

于是找了下面三个(最后我选择使用了:DOTween)。


unity tween libs

花了一个上午,看完了三个 tween 库的介绍与文档,总结:


DOTween

个人选择使用的是:DOTween,因为功能还是比较强大的。(说真的,这些所有的 Unity Tween 库使用起来都没有以前使用 AS3 的 greensock 那么顺手)


DOTween 使用的一些注意问题

协程中的 WaitForCompletion

如果 tween_show_loading 的动画足够短,并且直接 在 tween_show_loading 之前,还有一个比较长时间的动画,那么直接 yield return tween_show_loading.WaitForCompletion 可能会报一些警告:This Tween has been killed and is now invalid

代码如下:

    IEnumerator _StartHideLoading()
    {
        _KillRollingTrackerTween();

        tween_rolling_tracker = tracker.DOLocalMove(target_roll_tracker_move_in_pos, 1.0f)
            .SetEase(Ease.Linear);

        var seq = loadingBar.Hide();

        if (tween_rolling_tracker.active) yield return tween_rolling_tracker.WaitForCompletion();
        Debug.Log("after yield return tween_rolling_tracker.WaitForCompletion();");

        //if (seq.active) yield return seq.WaitForCompletion();
        yield return seq.WaitForCompletion();
        Debug.Log("after yield return seq.WaitForCompletion();");

        _KillHideLoadingSeq();

        tween_hide_loading = contentCG.DOFade(0.0f, 0.5f);

        yield return tween_hide_loading.WaitForCompletion();
        Debug.Log("after yield return seq_hide_loading.WaitForCompletion();");
        
        loadingRoot.SetActive(false);
    }

在这里插入图片描述

在这里插入图片描述

解决:如下面 判断一个 tween 是否完成的方式,在前面多加一句:if (xxx.active) 就可以了


判断一个 tween 是否完成

  • 第一:bool Tween.IsComplete() ; 但要确保你的 tween 不是 autoKill,默认所有的 tween 都是 autoKill 的,除非显示设置 AutoKill(false);
  • 第二:Tween.active 如果你的 tween 是 autoKill 的,那么可以使用 tween.active 来判断

下面的代码演示就是一个 autoKill 的tween 在判断是否完成了或有效的方法

// 如果还是有效的、激活的,或是未那么可以使用 WaitForCompletion 来等待完成
if (tween_show_loading.active) yield return tween_show_loading.WaitForCompletion();

kill 一个 tween

有还几种方式

tween.Kill();
DOTween.Kill(tween.id);
DOTween.Kill(tween.intId);
DOTween.Kill(tween.stringId);

但是最好还是使用:一个独立的 string 来保存起来,再 Kill

(为何要用 Kill,因为有些动画需要重复频率操作很多的特别需要各种 Kill,以前使用 AS3 就经常需要这么处理,不然动画会有冲突)

代码如下:

tween 同一时刻只有一个,可以这么写:

private void ShowXXX()
{
	DOTween.Kill($"{GetType().Name}"); // 如果你嫌弃效率问题,可以保存起来
	xxx.DOXXX().SetXXX().SetXXX().SetID($"{GetType().Name}");
}

如果 tween 对象可以有多个,那么需要其他的写法,或是使用 Sequence 来处理。

如果 Sequence 处理起来吃力,就使用 Unity 的 Coroutine + Sequence 来处理即可

可能有同学问,为何不使用 int 的方式,保存一个 int 比起一个 string 效率好多了,内存也少

但是我也试过了,会有问题,具体我也没去看 DOTween 开源的逻辑了,后面如果需要,还需要看它的 Git 代码,还考虑需要亲手去修改代码。

使用 int 的方式的 代码,如下:

private static int tween_id_counter;
private int tween_id = -1;
private void ShowXXX()
{
	if (tween_id != -1) {
		DOTween.Kill(tween_id);
	}
	tweenId = ++tween_id_counter;
	xxx.DOXXX().SetXXX().SetXXX().SetId(tweenId).OnComplete(()=>{tweenId = -1});
}

上面的 tween 同一时刻只能有一个,如果上次的没有运行完,那么就需要先将之前未完成的先 kill 掉。

我的代码曾经这样使用过,发现动画有异常。但是如果使用上面 string 的方式就没有问题。暂时没去查看代码(使用的是 .dll 的 DOTween)


Pause 与 Resume 不对称

有 Pause 但 “没有” Resume。注意打双引号的 没有

因为它的 Resume 还是使用 Play 来处理。

如,下伪代码:

private Tween tween;
private void ToggleTween()
{
	if (tween == null) 
	{
		tween = go_transform.DOMoveX(
				()=>the_x,value=>the_x=value,100,1.0f)
				.SetLoops(-1, LoopType.Restart));
	}
	else
	{
		// resume
		if (tween.isPlaying()) 	tween.Play().SetLoops(-1, LoopType.Restart); // 注意参数要重新设置
		// pause
		else 					tween.Pause();
	}
}

其中注意我的注释中的一句, resume 处理需要恢复参数,所以 DOTween 的 Resume 是用会 Play() 来处理的(我习惯了很多 Tween 库都是有 Resume 的,一下当时没找到,试了一下 Play,果然还真是,只不过,后来发现不会重复播放了,所以我有重新设置会参数,注意 Pause 后的 tween 再次 play 会从当前的一些数据继续播放,但是就是前面说的,一些状态参数又需要重新设置了,这些设置不应该的,按道理是可以做到不到重复设置状态参数的,但暂时没有兴趣去看源码)


OnComplete 在 Sequence 与 Tween 中使用

在 Tween 中使用如下代码:

trans.DOXXX().OnComplete(()=>{Debug.Log("Test1");});

一般不出什么意外 "Test1" 的输出还是比较确定的

但如果使用 Sequence:

var seq = DOTween.Sequence();
seq.Append(trans.DOXXX(....));
seq.Append(trans.DOXXX(....));
seq.AppendInterval(1.0f);
seq.OnComplete(()=>{Debug.Log("Test2");});

基本上补回输出 "Test2"

但如果 Sequence 完成的回调使用 AppendCallback 来替代就比较稳定

var seq = DOTween.Sequence();
seq.Append(trans.DOXXX(....));
seq.Append(trans.DOXXX(....));
seq.AppendInterval(1.0f);
seq.AppendCallback(()=>{Debug.Log("Test2");}); // 注意此句使用 AppendCallback

这样就可以输出 "Test2"

而我看了一下:Sequence 就一个空类,但是继承与 Tween 的,因为作者使用 public static RET_TYPE Extension_Func(this XXX xxx, ArgType1 .., ArgTypeN...); 的方式来扩展 Sequence 与 Tween 的方法来处理的

所以这些 OnComplete 的差异也是不应该的

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐