ZH-CN/OOP介绍: Difference between revisions

From Multi Theft Auto: Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(7 intermediate revisions by the same user not shown)
Line 5: Line 5:
__TOC__
__TOC__
== 关于OOP的介绍 ==
== 关于OOP的介绍 ==
OOP 是指 ''面向对象程序设计''. OOP是将所有函数都封装入每一个接口进行访问,这样的接口我们称之为实例。一个实例可以是由元素类、数据库类、玩家类、载具类等实例化出来对象。 一般来说,在Lua中,所有东西都是面向过程的,因此你会写出如下代码:
OOP 是指 ''面向对象程序设计''. OOP是将所有函数都封装入每一个接口进行访问,这样的接口我们称之为实例。一个实例可以是由元素类、数据库类、玩家类、载具类等实例化出来对象。  
 
类,即Class。实例(Instance)是对象(Object)。
 
用类创建对象(实例)称为实例化(Instantiate)。
 
一般来说,在Lua中,所有东西都是面向过程的,因此你会写出如下代码:
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
local vehicle = createVehicle(411, 0, 0, 3)
local vehicle = createVehicle(411, 0, 0, 3)
Line 14: Line 20:
destroyElement(vehicle)
destroyElement(vehicle)
</syntaxhighlight>
</syntaxhighlight>
More often than not, you know what you're dealing with. The variable almost always links back to the type, you would name a vehicle that exploded as '''explodedVehicle''', or at-least in context you would understand that '''exploded''' implies a vehicle when in the '''onVehicleExplode''' event. So you have to write a long function ''and'' refer to the vehicle manually when working procedurally. Painful. Object orientated programming is very different to this and works with each "object" individually. Instead of having to call a function and referencing it inside the call, you actually call the function INSIDE the class.
很多情况下,你可能知道你在干什么。变量通常和类型相关联,在'''onVehicleExplode'''事件中,如果让你给"一辆已经爆炸(exploded)的载具(Vehicle)"命名,你可能第一个想到的就是'''explodedVehicle''',或者至少在内容上很可能会受到来自事件中'''explode'''的暗示。因此,在面向过程的程序设计中,你需要写一个很长的函数,并且需要手动引用载具(explodedVehicle)。是不是感觉戴上了痛苦面具一样?面向过程的程序设计就有异于这种方法,而且它的编程方式是面向每一个"对象"。你不再需要调用一个函数,并且在这个函数中引用载具,而是可以直接在类中调用函数。


You probably think everything you can create and pass to functions are elements. A vehicle is an element. A player is an element. Anything that is an element is also a class. Connections create an instance of a class, but "'''connection'''" isn't an element, it's an instance - an object. Throughout the tutorial when I say ''object'', I don't mean [[createObject]] (unless I actually mention it), but to make things clearer I will avoid mentioning physical objects as I write this article. Here is a fancy venn diagram I created to show the simple organisation of classes and elements.
你可能会想你可以创建的并且能够传入函数的都是元素。比如一辆载具是元素,一个玩家是元素……任何东西只要是元素都存在对应的类。比如Vehicle创建了一个实例,但是"'''vehicle'''"并不是一个元素,而是一个实例(或者说是对象)。这边我给出一张关于类和元素的文氏图,希望能够让你有一个概念。
[[File:Classes,_Elements_and_Problem_children.png|500px|left|venn diagram]]
[[File:Classes,_Elements_and_Problem_children.png|500px|left|venn diagram]]
The functions on the left are sorted to show what kind of category the returned value rests in. We've got Classes, Elements and "Problem children".
Problem children aren't real categories written in the code, just functions that break rules. Everything you can play with are classes: resources, vehicles, and teams.
All elements are classes, you can do: <syntaxhighlight lang="lua">destroyElement(ped)</syntaxhighlight> but you can't do: <syntaxhighlight lang="lua">destroyElement(resource)</syntaxhighlight>


Problem children are weird things. You can't do all the functions mentioned in (actually, all elements don't allow the full assortment of functions to be applied to them, but I've especially mentioned a few of them) in the "Element functions" section of the functions list, but you can do destroyElement() on them.
左边的排列的函数呈现了返回值属于什么类型。我们得到了类,元素和“问题儿童”。
There are children of classes, for example, with players, the system goes like: "''Element -> Ped -> Player''". All Players are Peds and all Peds are Elements. Not all Peds are Players, and certainly not all Elements are Players. The main point here is that almost everything that you can create or retrieve and then reuse later use a class.
问题儿童在代码中并不是真正的分类,只是它们打破了正常的函数使用规则。资源(resources)、载具(vehicles)和队伍(teams),你能用的所有东西都是类。
所有的元素都是类,你可以这么干: <syntaxhighlight lang="lua">destroyElement(ped)</syntaxhighlight> 但是你不能这么干: <syntaxhighlight lang="lua">destroyElement(resource)</syntaxhighlight>


Instead of the code before, the code could be replaced with this:
这些“问题儿童”是比较奇怪的东西,你不能对它们使用在元素函数"Element functions"部分列出的函数,事实上所有的元素都不可以一个不落地使用所有的函数),但是你可以对它们使用destroyElement()函数。
类也存在子类,比如对于玩家(Player),关系类似于“''元素->行人->玩家''”(''Element -> Ped -> Player'')。所有的玩家(Player)都是行人(Ped),所有的行人(Ped)都是元素(Element)。不是所有的行人(Ped)都是玩家(Player),同样的,不是所有的元素(Element)都是玩家(Player)。最主要的一点是,你可以创建或取得的几乎所有东西都存在类。
 
上述代码可用以下代码代替:
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
local vehicle = createVehicle(411, 0, 0, 3)
local vehicle = createVehicle(411, 0, 0, 3)
Line 34: Line 41:
vehicle:destroy()
vehicle:destroy()
</syntaxhighlight>
</syntaxhighlight>
It works pretty similar to how a table works, it's just like customTable.setSomething() except the use of ''':''' makes Lua convert customTable:setSomething() convert to customTable.setSomething(customTable). This is pretty internal stuff about ''syntactic sugar'' and you don't really need to worry much about it.
它的工作原理和表(table)非常类似,就像customTable.setSomething(),只是''':'''是lua用来把customTable:setSomething()转化成customTable.setSomething(customTable)。这是lua本身自带的语法糖,你不需要特地关心这个。
 
那些函数都是非常有用的,但是对于OOP而言,变动比较多,我会在下面详细解释。


Those functions are pretty useful, but there are more changes with OOP, I'll explain this below.
== 实例化和变量 ==
OOP移除了函数的所谓"创建"的意义,取而代之的是实例化。例如原来是'''createVehicle''',在OOP中,我们只需要使用'''Vehicle''',它们以同一种方式运作,或者说,我们可以认为'''Vehicle = createVehicle'''。是不是很科幻,很花里胡哨呢?


== Instantiation, variables ==
它们的唯一区别就在于,使用OOP的情况下,你可以省略掉一些多余的部分,'''Vehicle'''没有这些多余的部分,但是玩家(Player)是肯定有的。比如本来要写成getPlayerFromName(),在OOP的语法下,只需要使用Player.getFromName()。是不是非常简单呢?这也确实是一种组织代码的好方法。
OOP removes the need to say the "create" part of the function, so instead of saying '''createVehicle''', you just say '''Vehicle'''. It works exactly the same way, it's almost just like doing '''Vehicle = createVehicle'''. Fancy, isn't it? The only difference here is that you miss out on the extra things offered, Vehicle doesn't have these extra things, but Player definitely does. For example, instead of doing getPlayerFromName(), you would do Player.getFromName(). It's a nice and simple way to organise functions.
{{Tip|''Vehicle()'' 也是可以用的,因为它会访问 '''Vehicle.create''' 函数,这样就能够让你在创建一个对象的时候省略 '''.create''' }}
{{Tip|''Vehicle()'' works because it actually accesses the '''Vehicle.create''' function, this allows you to omit the '''.create''' when simply "creating an object"}}
既然OOP比面向过程更高级,也已经继承了许多东西过来,但是为了化繁为简,所有函数对应的变量要求最多只能有一个输入。我们已经把'''getElementDimension()''' 缩短为 '''element:getDimension()''' 了,但是我们还可以更进一步: '''element.dimension'''。是不是很像个变量呢?它可以像普通的变量一样进行赋值,就比如:
Since OOP sits on top of procedural, many things have been inherited from the style of procedural, but to make things easier we have variables for all the functions that require a single input. We've shortened '''getElementDimension()''' down to '''element:getDimension()''', but we can also go one layer deeper: '''element.dimension'''. Yep, just like a variable. You can set this variable just like a normal variable and read from it just like a normal variable. Hey, you could even do this:
<syntaxhighlight lang="lua">local function incrementDimension()
<syntaxhighlight lang="lua">local function incrementDimension()
     local player = Player.getRandom() -- get a random player
     local player = Player.getRandom() -- 随机抽取一位幸运玩家
     player.dimension = player.dimension + 1 -- increment dimension
     player.dimension = player.dimension + 1 -- 维度增加1
end
end
setTimer(incrementDimension, 60*1000, 10) -- set a timer for sixty thousand milliseconds, sixty seconds, one minute
setTimer(incrementDimension, 60*1000, 10) -- 设置一个60秒的定时器,一共运行10次,每次运行调用函数incrementDimension
</syntaxhighlight>
</syntaxhighlight>
This code would take a random player and move them to the next dimension every minute for the next ten minutes.
上述代码设置了一个60秒的定时器,一共运行10次,也就是说在接下来的10分钟内每分钟都会随机抽取一位幸运玩家,让其维度+1。


== Vectors ==
== 向量 ==
player.position works too! But how do you change three arguments... using one variable? Vectors.
player.position当然也可以用!但是你怎么能够用一个变量。。。来同时改变三个参数?答案是 向量。
Vectors are very powerful classes and they come in multiple forms, for the purpose of this introduction I'll just cover a three dimensional vector in terms of elements. Using a vector is very simple, and is, of course, optional. Wherever you can currently use positions, you can use a vector.
向量类非常强大,他们有多种形式。为了能够在这里解释的清楚一点,我将会在接下来的例子中使用3维向量。使用向量也是一件特别简单的事,当然,如果你开心的话。只要是出现了位置(position)的地方,你就可以用向量。


So, this is a simple example of creating a vehicle and moving it to the the centre of the map using vectors
因此,下面这个例子是关于使用向量来创建一辆载具并且移动到地图中心。
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
-- First, create a three-dimensional vector
-- 首先,创建一个3维向量
local position = Vector3(300, -200, 2) -- some place far away
local position = Vector3(300, -200, 2) -- 远处某一点
local vehicle = Vehicle(411, position) -- create a vehicle at the position
local vehicle = Vehicle(411, position) -- 在这点创建一辆车
vehicle.position = centreOfMap - Vector3(300, -200, 0) -- move the vehicle two units above the center of the map
vehicle.position = centreOfMap - Vector3(300, -200, 0) -- 把这辆车移动到地图中心处上方两个单位。
</syntaxhighlight>
</syntaxhighlight>


Yes, I used the negative sign. Vectors aren't just fancy ways for positions or 3d rotations or whatever, you can use maths on them. The ''special'' maths hasn't been documented yet, but I'll try and work on that. So, as you can see in line one, I created a 3D vector at ''300, -200, 2'' and then in line two I created the vehicle at that position.
没错,我使用了减号。向量对于位置和旋转角度而言只是一个非常高级的工具,你可以通过向量对他们使用数学运算。所以,你能在第一行看到,我以坐标''300,-200,2''创建了一个3D向量,然后第二行我创建了一辆车在那个位置。


'''vehicle.position''' returned a vector and also takes a vector - it is pretty much setElementPosition() without the "()". Just a simple variable; so, in line three, I changed the vector value of the position of the vehicle. This is where the maths happened, in simple terms this is what is going on:
'''vehicle.position''' 返回一个向量,它有点像没有"()"的setElementPosition,仅仅是一个变量。因此,在第三行,我改变了这个代表着车辆坐标的。这里就是数学起作用的地方了,把上述向量运算展开就能得到如下等价的表达式:
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
x = 300 - 300
x = 300 - 300
Line 71: Line 80:
</syntaxhighlight>
</syntaxhighlight>


Vector maths is slightly complicated but it definitely allows for a wide variety of mathematical magic. Check out the useful links below related to Vectors and Matrices (Matrices = plural form of Matrix) to understand more about how this works.
向量的数学运算比较复杂,但是它无疑能够使用出数学的各种神奇的魔法效果,找一找有关向量和矩阵的实用页面链接,进去浏览一下文档,来帮助理解上述代码。


== Understanding the documentation==
==如何理解OOP文档==
The documentation for the OOP syntax intends to be very simplistic and is supported by the procedural syntax. To keep things simple, everything is consistently formatted in a certain way.
OOP语法的使用文档非常易懂,并且也是由面向过程的语法提供支持。为了尽可能保持简单,所有的文档都统一了格式:
<section name="Example" class="generic" show="true">
<section name="Example" class="generic" show="true">
{{OOP|Set the variable to nil to execute [[removePedFromVehicle]]|[[ped]]:warpIntoVehicle|vehicle|getPedOccupiedVehicle}}
{{OOP_ZH-CN|Set the variable to nil to execute [[removePedFromVehicle]]|[[ped]]:warpIntoVehicle|vehicle|getPedOccupiedVehicle}}
</section>
</section>


* Sometimes a note is added to the page. This will explain any special differences in the use of OOP for that function.
* 有的时候页面中会增加一个"提示"来说明此OOP函数的一些不同点以及特殊功能。
* Methods can either start like ''[[player]]:'' or ''[[Player]].'' - the former is only for a function on an instance (setPlayerHealth) and the latter is a static method (getRandomPlayer).
* 方法可以 以''[[player]]:'' 或者 ''[[Player]].'' 开头,前者代表玩家(Player)实例(player),比如(setPlayerHealth),后者是玩家(Player)类下的静态方法,比如(getRandomPlayer)
* The counterpart section this allows you to see at a glance how the variable can be used. In most cases this can be inferred from the function page.
* 对称函数能够允许你找到当前函数的对称部分,比如(setElementPosition)的对称函数为(getElementPosition)。这个在绝大部分情况下都是可以通过当前函数页面来推断出来的。


If you are a contributor to the wiki, please also consider reading [[Template:OOP|the OOP template]].
如果你想要对wiki做出一些贡献,请务必浏览[[Template:OOP_ZH-CN|简体中文OOP模板]],或者[[Template:OOP|英文版本的OOP模板]]


== Useful links ==
== 实用的页面连接 ==
Other useful OOP related pages:
其他实用的OOP相关的页面
* [[OOP]]
* [[OOP]]
* [[Vector]]
* [[Vector]]
Line 96: Line 105:


[[hu:OOP Introduction]]
[[hu:OOP Introduction]]
[[en:OOP Introduction]]
[[zh-cn:OOP介绍]]
[[zh-cn:OOP介绍]]

Latest revision as of 14:34, 3 February 2021

[[{{{image}}}|link=|]] Note: If you've contributed by editing and tweaking this forum page, if you've benefited from this tutorial, or if you have anything to say; please give me feedback in the forum link provided below.

此编程教程能够向你解释什么是OOP,教你如何来使用MTA的OOP功能。当前页面由thisdp翻译,英文原版由qaisjp撰写 Forum post.

关于OOP的介绍

OOP 是指 面向对象程序设计. OOP是将所有函数都封装入每一个接口进行访问,这样的接口我们称之为实例。一个实例可以是由元素类、数据库类、玩家类、载具类等实例化出来对象。

类,即Class。实例(Instance)是对象(Object)。

用类创建对象(实例)称为实例化(Instantiate)。

一般来说,在Lua中,所有东西都是面向过程的,因此你会写出如下代码:

local vehicle = createVehicle(411, 0, 0, 3)
setVehicleDamageProof(vehicle, true)
setElementFrozen(vehicle, true)
setElementHealth(vehicle, 1000)
setElementVelocity(vehicle, 0.2, 0.2, 0.2)
destroyElement(vehicle)

很多情况下,你可能知道你在干什么。变量通常和类型相关联,在onVehicleExplode事件中,如果让你给"一辆已经爆炸(exploded)的载具(Vehicle)"命名,你可能第一个想到的就是explodedVehicle,或者至少在内容上很可能会受到来自事件中explode的暗示。因此,在面向过程的程序设计中,你需要写一个很长的函数,并且需要手动引用载具(explodedVehicle)。是不是感觉戴上了痛苦面具一样?面向过程的程序设计就有异于这种方法,而且它的编程方式是面向每一个"对象"。你不再需要调用一个函数,并且在这个函数中引用载具,而是可以直接在类中调用函数。

你可能会想你可以创建的并且能够传入函数的都是元素。比如一辆载具是元素,一个玩家是元素……任何东西只要是元素都存在对应的类。比如Vehicle创建了一个实例,但是"vehicle"并不是一个元素,而是一个实例(或者说是对象)。这边我给出一张关于类和元素的文氏图,希望能够让你有一个概念。

venn diagram

左边的排列的函数呈现了返回值属于什么类型。我们得到了类,元素和“问题儿童”。 问题儿童在代码中并不是真正的分类,只是它们打破了正常的函数使用规则。资源(resources)、载具(vehicles)和队伍(teams),你能用的所有东西都是类。

所有的元素都是类,你可以这么干:

destroyElement(ped)

但是你不能这么干:

destroyElement(resource)

这些“问题儿童”是比较奇怪的东西,你不能对它们使用在元素函数"Element functions"部分列出的函数,事实上所有的元素都不可以一个不落地使用所有的函数),但是你可以对它们使用destroyElement()函数。 类也存在子类,比如对于玩家(Player),关系类似于“元素->行人->玩家”(Element -> Ped -> Player)。所有的玩家(Player)都是行人(Ped),所有的行人(Ped)都是元素(Element)。不是所有的行人(Ped)都是玩家(Player),同样的,不是所有的元素(Element)都是玩家(Player)。最主要的一点是,你可以创建或取得的几乎所有东西都存在类。

上述代码可用以下代码代替:

local vehicle = createVehicle(411, 0, 0, 3)
vehicle:setDamageProof(true)
vehicle:setFrozen(true)
vehicle:setHealth(1000)
vehicle:setVelocity(0.2, 0.2, 0.2)
vehicle:destroy()

它的工作原理和表(table)非常类似,就像customTable.setSomething(),只是:是lua用来把customTable:setSomething()转化成customTable.setSomething(customTable)。这是lua本身自带的语法糖,你不需要特地关心这个。

那些函数都是非常有用的,但是对于OOP而言,变动比较多,我会在下面详细解释。

实例化和变量

OOP移除了函数的所谓"创建"的意义,取而代之的是实例化。例如原来是createVehicle,在OOP中,我们只需要使用Vehicle,它们以同一种方式运作,或者说,我们可以认为Vehicle = createVehicle。是不是很科幻,很花里胡哨呢?

它们的唯一区别就在于,使用OOP的情况下,你可以省略掉一些多余的部分,Vehicle没有这些多余的部分,但是玩家(Player)是肯定有的。比如本来要写成getPlayerFromName(),在OOP的语法下,只需要使用Player.getFromName()。是不是非常简单呢?这也确实是一种组织代码的好方法。

[[{{{image}}}|link=|]] Tip: Vehicle() 也是可以用的,因为它会访问 Vehicle.create 函数,这样就能够让你在创建一个对象的时候省略 .create

既然OOP比面向过程更高级,也已经继承了许多东西过来,但是为了化繁为简,所有函数对应的变量要求最多只能有一个输入。我们已经把getElementDimension() 缩短为 element:getDimension() 了,但是我们还可以更进一步: element.dimension。是不是很像个变量呢?它可以像普通的变量一样进行赋值,就比如:

local function incrementDimension()
    local player = Player.getRandom() -- 随机抽取一位幸运玩家
    player.dimension = player.dimension + 1 -- 维度增加1
end
setTimer(incrementDimension, 60*1000, 10) -- 设置一个60秒的定时器,一共运行10次,每次运行调用函数incrementDimension

上述代码设置了一个60秒的定时器,一共运行10次,也就是说在接下来的10分钟内每分钟都会随机抽取一位幸运玩家,让其维度+1。

向量

player.position当然也可以用!但是你怎么能够用一个变量。。。来同时改变三个参数?答案是 向量。 向量类非常强大,他们有多种形式。为了能够在这里解释的清楚一点,我将会在接下来的例子中使用3维向量。使用向量也是一件特别简单的事,当然,如果你开心的话。只要是出现了位置(position)的地方,你就可以用向量。

因此,下面这个例子是关于使用向量来创建一辆载具并且移动到地图中心。

-- 首先,创建一个3维向量
local position = Vector3(300, -200, 2) -- 远处某一点
local vehicle = Vehicle(411, position) -- 在这点创建一辆车
vehicle.position = centreOfMap - Vector3(300, -200, 0) -- 把这辆车移动到地图中心处上方两个单位。

没错,我使用了减号。向量对于位置和旋转角度而言只是一个非常高级的工具,你可以通过向量对他们使用数学运算。所以,你能在第一行看到,我以坐标300,-200,2创建了一个3D向量,然后第二行我创建了一辆车在那个位置。

vehicle.position 返回一个向量,它有点像没有"()"的setElementPosition,仅仅是一个变量。因此,在第三行,我改变了这个代表着车辆坐标的。这里就是数学起作用的地方了,把上述向量运算展开就能得到如下等价的表达式:

x = 300 - 300
y = -200 - -200
z = 2 - 0

向量的数学运算比较复杂,但是它无疑能够使用出数学的各种神奇的魔法效果,找一找有关向量和矩阵的实用页面链接,进去浏览一下文档,来帮助理解上述代码。

如何理解OOP文档

OOP语法的使用文档非常易懂,并且也是由面向过程的语法提供支持。为了尽可能保持简单,所有的文档都统一了格式:

Click to collapse [-]
Example

OOP 语法 什么是OOP?

提示: Set the variable to nil to execute removePedFromVehicle
方法: ped:warpIntoVehicle(...)
变量: .vehicle
对称函数: getPedOccupiedVehicle
  • 有的时候页面中会增加一个"提示"来说明此OOP函数的一些不同点以及特殊功能。
  • 方法可以 以player: 或者 Player. 开头,前者代表玩家(Player)实例(player),比如(setPlayerHealth),后者是玩家(Player)类下的静态方法,比如(getRandomPlayer)。
  • 对称函数能够允许你找到当前函数的对称部分,比如(setElementPosition)的对称函数为(getElementPosition)。这个在绝大部分情况下都是可以通过当前函数页面来推断出来的。

如果你想要对wiki做出一些贡献,请务必浏览简体中文OOP模板,或者英文版本的OOP模板

实用的页面连接

其他实用的OOP相关的页面