首个里程碑顺利结束

到今天为止,第一个里程碑算是顺利结束了。

其实周三就已经完成所有功能提交验收了,周四把GameLogic的执行顺序修正了一下,保证Component的开关是可用的。之前的做法保证勉强可用,这下应该是确保正确了。只是还遗留了两个小问题:1. 不能保证Update一定在Initialize、Enable、Start之后调用,因为Initialize和Start都有异步方法;2. 第一个问题的衍生问题,我在纠结要不要让两个异步方法不再等待,一个脚本在异步加载的时候,所有脚本都要等待加载完成,虽然并不会卡住主线程,但是总归是有点奇怪的。一时半会还没想通,先放着吧。

这两周代码总归还是写得挺开心的,状态很好以至于直接通宵了几天,上周心动48小时也是玩心大起也跟玩了两天,跟叶斌前辈以及邢大哥聊的收获颇丰,真的很感谢前辈们的分享。现在来看基本上框架算是完善一些了,也算是可以缓一口气。

网络那边交给诸谦去研究了,最后还真是觉得Skynet是比较靠谱的解决方案,现在已经(排除万坑XD)走通了基本的连接和加密方面,下周就是协议方面了,最后整个流程走通了,就可以准备进行整理和并入框架了。诸谦真的还是厉害!

放松了两天,可以过个闲适一些的周末。下周开始进入里程碑 2 的开发。

.Net的Task写起来太TM爽了

上周二拿到PvE的需求,开始搞起来了。

地形用新的TileMap搞了一下,先用着再管美术问题。

摇杆就用EasyTouch的,结果还是发现确实没法满足右摇杆的需求,于是就开始纠结怎么办了。

结果纠结了一下午之后,完美解决了。

RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, screenPosition, null, out localPosition);

获取实际点击位置,分辨率无关。顺便再重构了一下InputController,逻辑非常清晰,很满意。

主角状态有意没有用很复杂的结构,先简单写了一些行为,然后对输入做绑定。前期需求还不明确的情况下设计逻辑架构感觉其实还是挺不合算的。子弹也是一样,简单写了个实现。

上周基本就是这样。

其实整个框架一直都有一个严重的问题,就是基于协程的异步接口一直令人非常难受。主要的点在于,协程不能返回值啊!!!一溜烟写一串那确实是挺爽的,但是一旦需要返回值那就直接火葬场。框架服务启动的时候会涉及AssetBundle加载,肯定涉及异步,代码写起来的感觉啊,真的是拆东墙补西墙,一个地方改改一堆地方都要跪。今天一早来,改到中午,好不容易把View的加载改好了(本来是Resources.Load),结果好了,这里不阻塞了,直接服务启动速度快了很多,导致整个服务都启动好了,主角还没被加载出来,然后一加载敌人,找不到主角,跪了。

虽然这个问题可以简单的靠敌人做一下null判断就可以解决的事情,但是总是心里非常的不爽。明明弄了这么一套服务框架,出发点就是让执行顺序可以得到保证,而且明明大部分情况都是可以的,但是最后的一点问题总是不好解决,非常的难受。

而主要的问题,就是协程不能返回值啊!!!要让我把所有代码都写成一个Request形式的,也太难受了,而且这样,其实说到底还是得在一个协程里完成,只是可以把这个协程往上推,根本不解决实际问题。

崩溃,这个问题想了很久很久,感觉实在没法想出什么写法可以完美解决这个问题的。

最后,我也不记得是怎么想起来的,用.Net 4.6 Framework试一下Task。

随便Google了一下,找到了个以及作者的博客。Asset Store也有,但是不是最新提交,于是git clone了一份下来。在经历了导进去删掉再导进去再删掉最后还是导了进去之后,终于搞清楚是怎么回事了。

于是我在办公室里喊了一下午:“太爽了!!!”

所以就不能怪大家今天一直用奇怪的眼神看我了……

大家直接理解为,新的语法可以把任何一个函数变成异步的(只需要加上async关键字),然后,可以随意返回值!!!

写出来类似这样:

public class AsyncExample : MonoBehaviour
{
    public async void Start()
    {
        // Wait one second
        await new WaitForSeconds(1.0f);
 
        // Wait for IEnumerator to complete
        await CustomCoroutineAsync();
 
        await LoadModelAsync();
 
        // You can also get the final yielded value from the coroutine
        var value = (string)(await CustomCoroutineWithReturnValue());
        // value is equal to "asdf" here
 
        // Open notepad and wait for the user to exit
        var returnCode = await Process.Start("notepad.exe");
 
        // Load another scene and wait for it to finish loading
        await SceneManager.LoadSceneAsync("scene2");
    }
 
    async Task LoadModelAsync()
    {
        var assetBundle = await GetAssetBundle("www.my-server.com/myfile");
        var prefab = await assetBundle.LoadAssetAsync<GameObject>("myasset");
        GameObject.Instantiate(prefab);
        assetBundle.Unload(false);
    }
 
    async Task<AssetBundle> GetAssetBundle(string url)
    {
        return (await new WWW(url)).assetBundle
    }
 
    IEnumerator CustomCoroutineAsync()
    {
        yield return new WaitForSeconds(1.0f);
    }
 
    IEnumerator CustomCoroutineWithReturnValue()
    {
        yield return new WaitForSeconds(1.0f);
        yield return "asdf";
    }
}

这是这个库的官方样例,再次感叹拓展方法真的太神奇,可以和协程完美互相结合。

我只能说,谁用谁知道。

很快把项目所有异步接口都改写成Task,服务和游戏逻辑完全保证执行顺序,完美达成目的不说,关键写起来是真的优雅,真的爽。

开心,晚饭汉堡王30-20都没能令我更开心。