10月9日到10月30日学习总结

文章目录

  • 关于输入法开发上问题与解决
    • `context.GetSelection`报错`System.StackOverflowException`:
    • 输入法送入宿主程序文本,插入位置与光标位置的问题。
  • AI+与游戏开发
  • 《游戏编程模式》笔记
    • 设计模式
      • 命令模式
      • 撤销与重做
      • 享元模式
  • Unity与Lua同舞
    • 何为Unity
    • 何为Lua
    • Unity X Lua = XLua
    • 为什么选择它
    • 设计思路
    • 关于xlua反复报错This delegate/interface must add to CSharpCallLua : XXX的问题
    • 关于Unity帧数不固定问题

关于输入法开发上问题与解决

context.GetSelection报错System.StackOverflowException:

C#与C++通信需要内存对齐。托管代码与非托管代码之间需要封送数据。需要运用MarshalAs特性1

​ 此属性是可选的,因为每个数据类型都有默认的封送处理行为。 仅当给定类型可以封送到多种类型时,此属性才是必需的。

接口ITfContext中函数需要改为HRESULT GetSelection([In] TfEditCookie ec, [In] uint ulIndex, [In] uint ulCount, [Out] TF_SELECTION[] pSelection, [NullAllowed] out uint pcFetched);函数需要改为HRESULT GetSelection([In] TfEditCookie ec, [In] uint ulIndex, [In] uint ulCount, [Out,MarshalAs(UnmanagedType.LPArray)] TF_SELECTION[] pSelection, [NullAllowed] out uint pcFetched);

输入法送入宿主程序文本,插入位置与光标位置的问题。

之前使用的是ITfContext::GetStart获取到ITfRange,调用ITfRange::SetText来插入文字。插入的位置默认为文章首,个别程序会插入到光标处,并且后退(eg. Windows记事本)。

后改用ITfcontext::GetSelection获取TF_fSelection实例后,通过获取range属性来获取ITfRange。解决了插入问题。

但是,这里出现了光标不向后移动的问题。就算给TfAnchor设置了重力和合并,均改变不了输入以后光标的位置。最后,找到一个开源的输入法——狼毫输入法2——通过阅读源码,才解决问题(ps 通过阅读源码,感觉这个开源代码的写的挺优美的。简简单单的几行就解决问题了。

修改后的相关代码如下:

_selections = new TF_SELECTION[1];
            var res = _context.GetSelection(ec, 4294967295, 1, _selections, out var pcFetched);
if (pcFetched == 1)
{
     var range = _selections[0].range;
    res = range.SetText(ec, 0, _words, _words.Length);
    range.Collapse(ec, TfAnchor.TF_ANCHOR_END);
    var selection = new TF_SELECTION
    {
        range = range,
        style = new TF_SELECTIONSTYLE
        {
            ase = TfActiveSelEnd.TF_AE_END,
            fInterimChar = false
        }
    };
    _context.SetSelection(ec, 1, selection);
}

AI+与游戏开发

在2002年,李晓磊等人在动物群体智能行为研究的基础上提出的一种新型方盛优化算法,该算法根据水域中鱼生存数目最多的地方就是本水域中富含营养物质最多的地方这一特点来模拟鱼群的觅食行为而实现寻优。算法主要利用鱼的三大基本行为:觅食、聚群和追尾行为,采用自上而下的寻优模式从构造个体的底层行为开始,通过鱼群中各个体的局部寻优,达到全局最优值在群体中凸显出来的目的3

现在人工智能成功用于游戏的案例有4

  • 孤岛惊魂5 利用AI自动生成地形植被
  • 荒野大镖客 NPC自主交互
  • 动物生态、实时环境渲染

此外还有AI修复古建筑5

《游戏编程模式》笔记

设计模式

命令模式

将一个请求封装为一个对象,从而允许你使用不同的请求、队列或者日志将客户端参数化,同时支持请求操作的撤销与恢复。

命令就是面向对象的回调。

对应代码c++

// 基类
class Command
{
    public:
    virtual ~Command(){}
    virtual void execute(GameActor& actor)=0;
};
class JumpCommand: public Command
{
    public:
    virtual void execute(GameActor& actor){
        actor.jump();
    }
}
class FireCommand :public Command
{
    virtual void execute(GameActor& actor){
        actor.fireGun();
    }
}
// ...
// 空值命令对象模式
class NullCommand: public Command
{
    virtual void execute(GameActor& actor){}
}
// 输入处理
class InputHandler
{
    public:
    Command* handleInput();
    // 绑定其他命令
    private:
    Command* buttonX_;
    Command* buttonY_;
    Command* buttonA_;
    Command* buttonB_;
}
void InputHnadler: handleInput()
{
    if (isPressed(BUTTON_X)) return buttonX_;
    else if (isPressed(BUTTON_Y)) return buttonY_;
    else if (isPressed(BUTTON_A)) return buttonA_;
    else if (isPressed(BUTTON_B)) return buttonB_;
    
}

// 角色执行命令
Command* command = inputHandler.handleInput();
if(command)
{
    command->exectue(actor);
}

在命令与角色之间加入间接层,可以使得玩家以及AI操控任意的角色。如果我们把命令序列化,便可以通过网络发送数据流,这个是多人游戏中主要的一部分。

撤销与重做

c++:

// 基类
class Command
{
    public:
    virtual ~Command(){}
    virtual void execute(GameActor& actor)=0;
    virtual void undo()=0;
};
class MoveUnitCommand: public Command
{
    public:
    MoveUnitCommand(Unit* unit,int x,int y): unit_(unit),x_(x),y_(y)
    {}
    virtual void execute()
    {
        unit_->moveTo(x_,y_);
    }
    private:
    Unit* unit_;
    int x_;
	int y_;
}

与备忘录相比,节约内存。

重做在游戏中不常见,但回放很常见。

JS实现(使用闭包)

function makeMoveUnitCommand(unit,x,y){
    var xBefore,yBefore;
    return function(){
        execute:function(){
            xBefore = unit.x();
            yBefore = unit.y();
            unit.moveTo(x,y);
        },
        undo: function(){
            unit.moveTo(xBefore,yBefore)
        }
    }
}
  • 如果有很多不同的命令类,可以定义一个具体的基类来实现更高层次的方法。命令主要的execute()变成了子类沙盒。
  • 如果在对象分层等那些不明确执行命令的角色情况下,可以将命令下放給其从属对象。可以参考责任链。
  • 同样的命令实例重复,可以使用享元模式。

享元模式

class TreeModel
{
    private:
    Mesh mesh_;
    Texture bark_;
    Texture leaves_;
}
class Tree
{
    private:
    TreeModel* model_;
    
    Vector position_;
    double height_;
    double thicknes_;
    Color barkTint_;
    Color leafTint_;
}

与类型对象模式对比:

	类型对象通过把“类型”对象化,可以尽可能较少定义新类型的数量,而享元模式却更加注重效率。

应用:

TiledMap

* 一般来说,享元对象总是不可变的。

  • 按需创建享元。在接口体上封装,来隐藏构造函数。可以参考工厂方法模式。
  • 查找实例化的享元,对象池模式。
  • 状态模式下,使用享元。以及上一章节提到的命令模式。

Unity与Lua同舞

何为Unity

Unity为Unity Technologies 公司开发的三维游戏制作引擎。其凭借自身的跨平台性与开放性优势已经逐渐成为当今世界范围内的主流游戏引擎6。由于手机游戏的流行,目前2D游戏开发的需求量也越来越大了,因此Unity3D游戏引擎也增加了2D游戏开发的支持7

何为Lua

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能8

Unity X Lua = XLua

xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用9

为什么选择它

原因为游戏为横版弹幕游戏设计,弹幕的子弹变化万千,犹如夜空中中热烈绽放的烟花一样,绚丽多彩。需要一个高效的解释性语言去控制每个子弹和怪物的没帧逻辑。而Lua正好胜任此工作。

设计思路

设计为如下:10月9日到10月30日学习总结_第1张图片

由于Lua没有类继承,因此写了Class函数来生成实例,利用原型链来实现继承。核心代码如下:

-- 对应新类
function Class(base)
    base = base or object
    local result = {}
    result.is_class = true
    result.init = base.init
    result.frame = base.frame
    result.kill = base.kill
    result.base = base
    --setmetatable(result,_met)
    return result
end

生成的实例可以由New来直接运行:

function New(obj, ...)
    local new = Class(obj)
    new:init(...)
    return new
end

逻辑定义全在init,由Lua协程来控制每帧操作:

10月9日到10月30日学习总结_第2张图片

关于xlua反复报错This delegate/interface must add to CSharpCallLua : XXX的问题

添加CSharpCallLua标签。比如:

public class LuaObject : MonoBehaviour
{
	[XLua.CSharpCallLua]
    public delegate void KillDelegate(int index);
	[CSharpCallLua]
    private delegate void DoFrameDelegate();
	private LuaEnv luaEnv = null;
    private DoFrameDelegate DoFrame;
	public KillDelegate Kill;
	void Start()
	{
		//...
		DoFrame = luaEnv.Global.Get<DoFrameDelegate>("DoFrame");
        Kill = luaEnv.Global.GetInPath<KillDelegate>("Kill");
	}
}

如果还是存在问题的话,清除并重新生成代码。

10月9日到10月30日学习总结_第3张图片

  1. Xlua-> Clear Generated Code

  2. Xlua-> Generate Code

    即可解决问题。

关于Unity帧数不固定问题

这边只更新Lua更新部分

void Update()
{
    if (IsRunning)
    {
        luaEnv?.Tick();
        var current = Time.realtimeSinceStartup;
        var needUpdateframeNum = (current - startTime) * 60 - frameNum;
        if(needUpdateframeNum>45)
        {
            // 卡
            Debug.LogError("太卡了!");
            // 勉勉强强让玩家继续
            needUpdateframeNum = 1;
            frameNum = 0;
            startTime = Time.realtimeSinceStartup;

        }
        for (var i=0;i<needUpdateframeNum;i++)
        {
            DoFrame();
            frameNum++;
        }
    }
}

  1. MarshalAsAttribute 类 (System.Runtime.InteropServices) | Microsoft Docs https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshalasattribute?view=netframework-4.8 ↩︎

  2. rime/weasel: 【小狼毫】Rime for Windows ↩︎

  3. 人工鱼群算法详解_wp_csdn的专栏-CSDN博客 https://blog.csdn.net/wp_csdn/article/details/54577567 ↩︎

  4. 《游戏行业发展概况》 https://game.academy.163.com/live?id=ga-1601284961961 ↩︎

  5. AI还原古代遗迹,一文多图带你穿越时空!_进行 https://www.sohu.com/a/413542857_587438 ↩︎

  6. Unity 3D简介 http://c.biancheng.net/unity3d/10/ ↩︎

  7. [Unity2D]游戏引擎介绍_weixin_34204057的博客-CSDN博客 https://blog.csdn.net/weixin_34204057/article/details/86275226 ↩︎

  8. Lua 教程 | 菜鸟教程 https://www.runoob.com/lua/lua-tutorial.html ↩︎

  9. Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. https://github.com/Tencent/xLua ↩︎

你可能感兴趣的:(经验分享)