ZH-CN/OOP介绍
此编程教程能够向你解释什么是OOP,教你如何来使用MTA的OOP功能。当前页面由thisdp翻译,英文原版由qaisjp撰写 Forum post.
关于OOP的介绍
OOP 是指 面向对象程序设计. OOP是将所有函数都封装入每一个接口进行访问,这样的接口我们称之为实例。一个实例可以是由元素类、数据库类、玩家类、载具类等实例化出来对象。
一般来说,在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)
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.
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.
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:
destroyElement(ped)
but you can't do:
destroyElement(resource)
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.
Instead of the code before, the code could be replaced with this:
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()
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.
Those functions are pretty useful, but there are more changes with OOP, I'll explain this below.
Instantiation, variables
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() works because it actually accesses the Vehicle.create function, this allows you to omit the .create when simply "creating an object" |
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:
local function incrementDimension() local player = Player.getRandom() -- get a random player player.dimension = player.dimension + 1 -- increment dimension end setTimer(incrementDimension, 60*1000, 10) -- set a timer for sixty thousand milliseconds, sixty seconds, one minute
This code would take a random player and move them to the next dimension every minute for the next ten minutes.
Vectors
player.position当然也可以用!但是你怎么能够用一个变量。。。来同时改变三个参数?答案是 向量。 向量类非常强大,他们有多种形式。为了能够在这里解释的清楚一点,我将会在接下来的例子中使用3维向量。使用向量也是一件特别简单的事,当然,如果你开心的话。只要是出现了位置(position)的地方,你就可以用向量。
So, this is a simple example of creating a vehicle and moving it to the the centre of the map using vectors
-- First, create a three-dimensional vector local position = Vector3(300, -200, 2) -- some place far away local vehicle = Vehicle(411, position) -- create a vehicle at the position vehicle.position = centreOfMap - Vector3(300, -200, 0) -- move the vehicle two units above the center of the map
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.
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:
x = 300 - 300 y = -200 - -200 z = 2 - 0
向量的数学运算比较复杂,但是它无疑能够使用出数学的各种神奇的魔法效果,找一找有关向量和矩阵的实用页面链接,进去浏览一下文档,来帮助理解上述代码。
如何理解OOP文档
OOP语法的使用文档非常易懂,并且也是由面向过程的语法提供支持。为了尽可能保持简单,所有的文档都统一了格式:
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相关的页面