• 使用C#对Godot属性进行改写(其实是覆盖)


    本文地址:https://www.cnblogs.com/oberon-zjt0806/p/14568221.html

    背景

    我还真没想到C#这个鬼东西还能这么用……

    起初是想通过某种办法改写一下Labeltext属性的赋值机制来改写一个自己的Label运作方式,之前想尽办法用GDScript改写set_text(实际情况是GDScript下根本不提供这个函数)和_set(这玩意改完了不对赋值操作起作用,感觉更像是给反射用的)然而并没有什么卵用……虽然说直接调用那些函数的方式还是可以使用的,但是写法上我还是喜欢用直接赋值的来得简洁。

    但总之我希望修改对于text属性赋值时的运作方式,我甚至去想写NativeScript直接把Label重写一下,不过NativeScript在官方文档里的一顿操作猛如虎我愣是没看懂,Native这块我先放着,哪天用得上哪天再研究。(但我估计重写还是能办到的)

    好在我下的Godot版本是mono版(虽然官方到了3.3rc6的文档里都说C#绑定得不太完善,不过考虑到随时有可能出现需要C#的情形以及mono版也不耽误写GDScript的情况下我就下的mono版,Steam上没有mono版也就罢了),所以我又重新考虑C#是否可以解决这个问题。

    改造方法

    就以改写Label.text的运作方式为例……

    Godot源码是C++的,GDScript里面怎么做的封装我不太清楚,但总之只剩下了text属性变量,因为GDScript并不允许对属性重定义,所以不能用setget来指定setter和getter函数(因为文档规定setget只能在var声明句中使用,但GDScript的语法又不让重声明,至少不能再次声明同名的属性)。

    不过在C++的源码中,Godot实际上是有set_textget_text的,而且这两个函数被表述为public,所以就很迷……

    // setter
    void Label::set_text(const String &p_string) {
    	if (text == p_string) {
    		return;
    	}
    	text = p_string;
    	xl_text = tr(p_string);
    	dirty = true;
    	if (percent_visible < 1) {
    		visible_chars = get_total_character_count() * percent_visible;
    	}
    	update();
    }
    // getter
    String Label::get_text() const {
    	return text;
    }
    

    至于为什么在GDScript里没了,可能是跟某种绑定机制有关,不过这个改天再看看。

    不过用C#脚本的话,情况就不一样了,因为在C#里对应的Label.Text就是C#概念下的Property,而且由于C++源码里是public(事实上也必须是,因为这东西可改),所以Label里的绑定多半写的也肯定是:

    public class Label : Control
    {
    	//...
    	public string Text { get{/*...*/} set{/*...*/} }
    	//...
    }
    

    本来我想用override把原来版本的Text属性直接推翻重写,不过因为Label.Text并非virtual所以我不能推翻,只能强制隐藏……

    public class MyLabel : Label
    {
    	public new String Text
    	{
    		get {/*...*/}
    		set {/*...*/}
    	}
    	//...
    }
    

    不过另外一个问题来了,getter和setter怎么重写的问题……

    本来傻fufu的想直接对新的Text做手脚结果运行的时候Godot调试器直接卡崩溃了……

    报错告诉我内存空间爆了,那估计是被当成无尽递归闹得,于是我就改成了给父类捆绑的方式……

    public class MyLabel : Label
    {
    	public new String Text
    	{
    		get => base.Text;
    		set 
    		{
    			// ...
    			base.Text = value;
    			// ...
    		}
    	}
    	//...
    }
    

    成功了,当我操作Text的时候,我可以在此基础上改写这些性质。

    当然了,这种做法有局限:

    1. 涉及底层API的操作很难复现,所以除非保留对base属性的赋值句base.XXX=value;,因为base属性也是相当于一个名字带两个函数,直接执行这个语句会连同原有的操作也做一遍,但是有些底层属性对继承类是private的,继承类看不到。
    2. 如果彻底不想使用原有的运作机制的话,那可能还需要引入字段来解决这个问题。(但是实际上这跟独立重开一个属性没差,只是不用换名字了而已)
    3. 结合上述两点,这种方法可能无法保留局部的原有机制,也就是说要么全用要么全不用,你只能在原有的基础上叠加,如果真的想达到真正意义上的重写可能还得下到底层去……

    作者:Oberon
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Java反射机制
    两个port贴合七夕主题,百度输入法的“情感营销”策略
    2014年百度之星程序设计大赛
    Java内存模型
    远程推送
    【NOI2010】海拔【平面图最小割】
    句法模式识别(二)-正规文法、上下文无关文法
    通过c# 实现mysql 数据库的备份和附加
    时光轴二之RecyclerView版时光轴效果
    手游产品经理初探(四)从Buybutton谈玩家付费
  • 原文地址:https://www.cnblogs.com/oberon-zjt0806/p/14568221.html
Copyright © 2020-2023  润新知