GDScript:动态语言简介

Gorgeous3Dwithgodot

关于

对于没有或几乎没有动态类型语言经验的程序员来说,它特别有用。

动态性动态类型的优缺点

GDScript是一种动态类型语言。因此,它的主要优点是:

这种语言简单易学。

大多数代码都可以快速地编写和更改,而且没有任何麻烦。

编写更少的代码意味着要修复的错误和失误更少。

更容易阅读代码(减少混乱)。

测试不需要编译。

运行时(Runtime)很小。

本质上是鸭子类型和多态性。

主要缺点是:

比静态类型语言的性能要低。

重构更加困难(无法追踪符号)

在静态类型语言中,一些通常在编译时检测到的错误只会在运行代码时出现(因为表达式解析更严格)。

代码完成的灵活性较低(一些变量类型只在运行时知道)。

这转化为现实,意味着Godot+GDScript是一个旨在快速高效创建游戏的组合。对于计算量很大并且无法从引擎内置工具(例如向量类型、物理引擎、数学库等)中受益的游戏,也存在使用C++的可能性。这允许您依旧使用GDScript创建游戏的绝大部分,并在需要性能的地方添加少量C++代码。

变量与赋值

动态类型语言中的所有变量都是类似变体的。这意味着它们的类型不是固定的,只能通过赋值修改。示例:

静态的:

inta;//=5;//="Hi!";//Thisisinvalid.

动态的:

varaValid,'a'="Hi!"_value("Hello"))中,变量和变量的指针/引用之间存在区别。后者通过传递对原始函数的引用,允许其他函数修改该对象。

在()_class(instance)Willbeunreferencedanddeleted.

在GDScript中,只有基本类型(int、float、string和vector类型)通过值传递给函数(值被复制)。其他所有(实例、数组、字典等)作为引用传递。继承Reference(如果没有指定任何内容,则默认从这里继承)的类在不使用时将被释放,但对手动继承自Object的类也允许手动管理内存。

数组

动态类型语言的数组内部可以包含许多不同的混合数据类型,并且始终是动态的(可以随时调整大小)。比较在静态类型语言中的数组示例:

int*array=newint[4];//[0]=10;//[1]=20;//Can'[2]=40;array[3]=60;//Can'_array(array);//[]array;//Mustbefreed.//orstd::vectorintarray;(4);array[0]=10;//[1]=20;//Can'[2]=40;array[3]=60;(3);//_array(array);//Passedreferenceorvalue.//Freedwhenstacks.

以及在GDScript中:

vararray=[10,"hello",40,60]_array(array)Freedwhennolongerinuse.

在动态类型语言中,数组也可以作为其他数据类型有多种用法,例如列表:

vararray=[](4)(5)_front()

或无序集合:

vara=20ifain[10,20,30]:print("Wehaveawinner!")
字典

字典是动态类型化语言中的一个强大工具。来自静态类型语言(例如C++或("Name:",d["name"],"Age:",d["age"])

字典也是动态的,键可以在任何一点添加或删除,花费很少:

d["mother"]="Rebecca"("name")BattleshipGameconstSHIP=0constSHIP_HIT=1constWATER_HIT=2varboard={}funcinitialize():board[Vector2(1,1)]=SHIPboard[Vector2(1,2)]=SHIPboard[Vector2(1,3)]=SHIPfuncmissile(pos):ifposinboard:Therewasaship![pos]=SHIP_HITelse:print("Alreadyhithere!")Nothing,[pos]=WATER_HITfuncgame():initialize()missile(Vector2(1,1))missile(Vector2(5,8))missile(Vector2(2,3))

字典还可以用作数据标记或快速结构。虽然GDScript字典类似于python字典,但它也支持Lua风格的语法和索引,这使得它对于编写初始状态和快速结构非常有用:

={name="John",age=22}print("Name:",,"Age:",)Indexingd["mother"]="Rebecca"="Caroline"(b,n)Willgofrombton-1,instepsofs.

一些静态类型的编程语言示例:

for(inti=0;i10;i++){}for(inti=5;i10;i++){}for(inti=5;i10;i+=2){}

转变成:

foriinrange(10):passforiinrange(5,10):passforiinrange(5,10,2):pass

反向循环是通过一个负计数器完成的:

for(inti=10;i0;i--){}

变成:

foriinrange(10,0,-1):pass
While

while()循环在任何地方都是相同的:

vari=0():print(strings[i])i+=1
自定义迭代器

在默认迭代器无法完全满足您的需求的情况下,您可以通过重写脚本中Variant类的_iter_init、_iter_next、和_iter_get函数来创建自定义迭代器。正向迭代器的一个示例实现如下:

classForwardIterator:varstartvarcurrentvarvarincrementfunc_init(start,stop,increment):====incrementfuncshould_continue():return(current)func_iter_init(arg):current=startreturnshould_continue()func_iter_next(arg):current+=incrementreturnshould_continue()func_iter_get(arg):returncurrent

它可以像任何其他迭代器一样使用:

varitr=(0,6,2)foriinitr:print(i)#Willprint0,2,and4.

确保在_iter_init中重置迭代器的状态,否则使用自定义迭代器的嵌套for循环将无法正常工作。

鸭子类型

当从静态类型语言迁移到动态类型语言时,最难掌握的概念之一是鸭子类型。鸭子类型使整个代码设计更加简单和直接,但是它的工作方式并不明显。

举个示例,想象一个大石头从隧道里掉下来,在路上砸碎了一切。在静态类型语言中石头的代码有点像:

voidBigRollingRock::on_object_hit(Smashable*entity){entity-smash();}

这样,任何能被岩石砸碎的东西都必须继承Smashable。如果一个角色、敌人、家具、小石块都易碎,它们需要从Smashable类继承,可能需要多次继承。如果不希望进行多重继承,那么它们必须继承像Entity这样的公共类。然而,如果只是其中几个能被粉碎的话,在Entity中添加一个虚拟方法smash()并不十分优雅。

使用动态类型的语言,这将不是问题。鸭子类型确保您只需在需要的地方定义一个smash()函数,就行了。无需考虑继承、基类等。

func_on_object_hit(object):()

就是这样。如果击中大岩石的对象有一个smash()方法,它将被调用。不需要考虑继承或多态性。动态类型化语言只关心具有所需方法或成员的实例,而不关心它继承什么或其类型。鸭子类型的定义应该使这一点更清楚:

“当我看到一只鸟像鸭子一样走路,像鸭子一样游泳,像鸭子一样呱呱叫时,我就叫它鸭子”

在这种情况下,它可转变成:

“如果物体可以被砸碎,不要在意它是什么,只管砸碎它。”

是的,称它为绿巨人(Hulk)类型似乎更合适。

有可能被击中的对象没有smash()函数。一些动态类型语言在方法不存在时,简单地忽略方法调用(如ObjectiveC),但是GDScript更严格,因此需要检查函数是否存在:

func_on_object_hit(object):_method("smash"):()

然后,简单地定义这个方法,岩石触碰的任何东西都可以被粉碎了。

免责声明:本文章如果文章侵权,请联系我们处理,本站仅提供信息存储空间服务如因作品内容、版权和其他问题请于本站联系