open.mp API design
Introduction
Trước tiên, một lời giải thích RẤT quan trọng - các tập lệnh hiện tại vẫn sẽ hoạt động như hiện tại. Chúng tôi đã làm việc rất chăm chỉ về khả năng tương thích ngược và ghi nhớ điều này trong mọi quyết định. Có nhiều cải tiến mà chúng tôi muốn thực hiện nhưng chúng tôi không thể thực hiện vì lý do này, và các mã khác phức tạp hơn nhiều do yêu cầu về khả năng tương thích này.
Tuy nhiên, có những cải tiến có thể thực hiện ở mọi nơi. Hãy cùng xem một số ví dụ về sự không nhất quán trong tập lệnh SA:MP:
Tags
Menu:CreateMenu
- Đã gắn thẻ.DB:db_open
- Đã gắn thẻ.CreateVehicle
- Chưa gắn thẻ.CreateActor
- Chưa gắn thẻ.
#define SELECT_OBJECT_GLOBAL_OBJECT 1
#define SELECT_OBJECT_PLAYER_OBJECT 2
forward OnPlayerSelectObject(playerid, type, objectid, modelid, Float:fX, Float:fY, Float:fZ);
type
không được gắn thẻ, cũng như TẤT CẢ các hằng số được SA:MP định nghĩa; không giống như các hằng số mặc định của pawn:
native File:fopen(const name[], filemode:mode = io_readwrite);
Naming
SetVehiclePos
- "Vehicle" ở giữa tên hàm.TextDrawTextSize
- "TextDraw" ở đầu.db_open
- "db" ở đầu và viết thường.fread
- "file" ở đầu và được rút gọn.asin
- Một hàm SA:MP được thêm vào mà không có chữ hoa/thường của camel.
Tính nhất quán:
GetVehicleZAngle
- "Z-Angle"GetVehicleRotationQuat
- "Rotation"SetPlayerFacingAngle
- "Facing Angle"SetObjectRot
- "Rot"
Mặc dù vậy, hầu hết các thư viện hiện đã thống nhất về quy ước đặt tên Module_Method
.
Constants
65535
Giá trị cho người chơi, diễn viên, TD và một số thứ khác không hợp lệ. Đây cũng là giá trị cho xe không hợp lệ, nhưng 0
CŨNG là ID xe không hợp lệ đôi khi được trả về.
0
Giá trị cho các tệp không hợp lệ, đôi khi là phương tiện (cũng như 65535
). Cũng là giá trị cho việc thiếu nhiều thứ như trạng thái hành động và vũ khí.
255
Giá trị cho các nhóm và menu không hợp lệ.
-1
Giá trị cho các vùng băng đảng và trạng thái vũ khí không hợp lệ.
Ngoài ra, nhiều thư viện sử dụng 0x80000000
và -1
cho các trạng thái không hợp lệ vì chúng ít có khả năng trở thành ID hợp lệ. 65535 là một con số khá lớn, nhưng một máy chủ lớn có thể dễ dàng có nhiều đối tượng hơn thế.
Per-Player Functions
CreateObject
vàCreatePlayerObject
Có phiên bản global và phiên bản dành cho từng người chơi.
SetPlayerMapIcon
Không có phiên bản global.
SetGravity
Không có phiên bản dành cho mỗi người chơi, mặc dù đây có thể là một trong những chức năng được yêu cầu nhiều nhất cho mỗi người chơi và được YSF cùng các plugin khác thêm vào gần như ngay lập tức.
CreateVehicle
Không có phiên bản cho mỗi người chơi, mặc dù cũng đã được yêu cầu nhiều lần. Nhưng cũng không được thêm vào bởi bất kỳ plugin (công khai) nào, thậm chí không phải plugin streamer.
SendClientMessage
vàSendClientMessageToAll
Có phiên bản toàn cục và theo người chơi, nhưng phiên bản theo người chơi là phiên bản mặc định không giống như hầu hết các hàm khác.
GangZoneShowForPlayer
vàGangZoneShowForAll
Menu, Gang Zones và Text Draws là các hàm SA:MP mặc định duy nhất mà bạn có thể chỉ định chính xác người chơi nào có thể nhìn thấy chúng. Tất cả những hàm khác là tất cả mọi người hoặc chỉ một người. Tất nhiên, các thư viện và plugin đã mở rộng rất nhiều về mô hình này và hầu hết các plugin tốt hiện nay đều cho phép kiểm soát rất chi tiết về các tập hợp con người chơi (nhóm) nào có thể sử dụng bất kỳ thực thể nào.
ID
CreateObject
vàCreatePlayerObject
Nhóm ID cho các hàm này được chia sẻ. Nếu một đối tượng toàn cục có ID 4
thì không đối tượng người chơi nào có thể có ID 4
, nhưng nhiều người chơi có thể có các đối tượng khác nhau, tất cả đều có ID 5
.
Create3DTextLabel
vàCreatePlayer3DTextLabel
ID được chia thành nhiều phần - 1024
ID đầu tiên là toàn cục, 1024
ID thứ hai là cho mỗi người chơi. Mỗi người chơi có thể có tối đa 2048
văn bản 3D, nhưng chỉ có 1024
của mỗi loại mặc dù thực tế là nó không tạo ra sự khác biệt nào ở phía máy khách.
SetPlayerMapIcon
ID được chỉ định thủ công, tối đa là 32. Trong một thời gian, giới hạn này không được kiểm tra ở phía máy khách, dẫn đến khả năng khai thác ACE.
ShowPlayerDialog
ID được chỉ định thủ công, không có giới hạn nào cả. ID cũng hoàn toàn vô nghĩa vì người chơi chỉ có thể nhìn thấy một hộp thoại cùng một lúc.
SetTimer
ID bao quanh, không kiểm tra xem bộ đếm thời gian hiện có có cùng ID hay không. Bạn sẽ phải bắt đầu lại 4.000.000.000 bộ đếm thời gian tại một thời điểm nào đó để gặp phải vấn đề này, nhưng điều đó có thể xảy ra - chúng thậm chí không cần phải tiếp tục chạy.
Và tất nhiên, một số người dựa vào ID được phân bổ theo thứ tự rất cụ thể, sau đó tự hỏi tại sao toàn bộ chế độ của họ bị hỏng khi họ thêm hoặc xóa một phương tiện duy nhất.
Compatibility
Vì vậy, một lần nữa, chúng ta phải làm rõ rằng tất cả "mã SA:MP" hiện có sẽ hoạt động. Điều đó có nghĩa chính xác là gì? Bất kỳ mã nào:
- Được viết bằng pawn.
- Sử dụng API SA:MP gốc mà không cần plugin.
- Được biên dịch lại bằng các include của chúng tôi.
- Đã sử dụng trình biên dịch cộng đồng.
Sẽ hoạt động.
Tuy nhiên:
Nếu bạn sử dụng plugin để viết bằng ngôn ngữ khác ngoài pawn, plugin đó có thể sẽ cần được chuyển trước. Vì vậy, mã của bạn sẽ không hoạt động ngay lập tức.
Nếu bạn sử dụng các plugin khác như streamer, YSF, âm thanh, v.v.; chúng có thể đã hoạt động, có thể cần chuyển hoặc có thể hoàn toàn không cần thiết vì chức năng của chúng đã được tích hợp vào máy chủ cốt lõi. Vì vậy, mã của bạn có thể hoạt động.
Nếu bạn chỉ có .AMX của chế độ của mình chứ không phải nguồn gốc, tại sao? Dù sao thì, trong khi tất cả các hàm SA:MP đều tồn tại, một số đã được làm lại hoặc thay thế bằng mã pawn hoặc macro và bạn PHẢI biên dịch lại. Vì vậy, nếu bạn không thể, mã của bạn sẽ không hoạt động.
Building
Eample
Trước tiên, chúng ta hãy xem xét chế độ SA:MP cực kỳ đơn giản.
#include <a_samp>
main() {}
public OnGameModeInit()
{
SetGameModeText("Example Script");
AddPlayerClass(0, 0.0, 0.0, 4.0, 0.0, 0, 0, 0, 0, 0, 0);
return 1;
}
public OnPlayerSpawn(playerid)
{
SetPlayerCheckpoint(playerid, 20.0, 20.0, 4.0, 2.0);
return 1;
}
public OnPlayerEnterCheckpoint(playerid)
{
SendClientMessage(playerid, 0xFF0000AA, "You won!");
return 1;
}
Bạn sinh ra. Bạn đến checkpoint. Bạn thắng.
Conversion
Để xây dựng điều này cho open.mp, chúng ta chỉ cần thay đổi include đầu tiên và thêm một define.
Cái này:
#include <a_samp>
main() {}
Trở thành:
#define OPENMP_COMPAT
#include <openmp\openmp>
main() {}
Lỗi đầu tiên bạn có thể gặp phải là:
open.mp scripts require the community compiler from: git.io/pawn-compiler
Nếu bạn gặp lỗi này, hãy truy cập https://git.io/pawn-compiler và tải xuống phiên bản trình biên dịch 3.10.10 trở lên. Đối với bản phát hành này, chúng tôi muốn có một công cụ tương đương pawno với trình biên dịch này đã được tích hợp sẵn, nhưng vẫn chưa thực hiện. Tôi NHIỆT LIỆT đề xuất bạn thử biên dịch chế độ của mình bằng trình biên dịch này trước vì nó sẽ bật lại cảnh báo về tính chính xác của const, do đó, bạn có thể sẽ nhận được một loạt cảnh báo mới ngay lập tức (đây KHÔNG phải là vấn đề với trình biên dịch, đây là những vấn đề trong mã của bạn vốn luôn tồn tại nhưng trước đó đã bị bỏ qua). Bạn cũng có thể muốn thay thế các include của mình bằng các include sau:
https://github.com/pawn-lang/pawn-stdlib
https://github.com/pawn-lang/samp-stdlib
Đó là một điều tốt để làm ngay cả khi bạn không sử dụng open.mp, vì chúng sửa một loạt các vấn đề về thẻ và const trong các include gốc.
Warnings
Nếu bạn không nhận được bất kỳ cảnh báo nào khi sử dụng trình biên dịch mới với phiên bản mới của a_samp, giờ đây bạn sẽ thấy một loạt các cảnh báo mới theo dòng sau:
`cảnh báo 234: hàm đã lỗi thời (ký hiệu "AddPlayerClass") Thay vào đó, hãy sử dụng "Class_Add".
Bạn có ba tùy chọn - và tất cả đều được hỗ trợ:
Bỏ qua các cảnh báo: Mã vẫn sẽ hoạt động.
Bỏ qua các cảnh báo: Đổi
OPENMP_COMPAT
thànhOPENMP_QUIET
:
#define OPENMP_QUIET
#include <openmp\openmp>
main() {}
- Sửa các cảnh báo: Một số hàm đã đổi tên để thống nhất với nhau; một số hàm đã thay đổi tham số vì các hàm cũ đã phát triển và không thể hiện đầy đủ khả năng của open.mp. Hiện tại vẫn chưa có cách đơn giản nào để chuyển đổi mọi hàm, nhưng bạn có thể để nguyên cảnh báo trong khi chuyển đổi mã chậm - các hàm cũ sẽ tiếp tục hoạt động hoàn hảo.
Có ba giai đoạn để chuyển đổi:
#define OPENMP_QUIET
Sử dụng định nghĩa này cho phép chế độ của bạn biên dịch mà không có cảnh báo mới từ các hàm đã lỗi thời. Nhưng bạn không nên tuân theo định nghĩa này và các chuyển đổi ngầm định sẽ chỉ hoạt động với mã pawn. Bạn có thể chuyển đổi mã trong chế độ này vì API mới cũng sẽ hoạt động, nhưng bạn không thể sử dụng trình biên dịch để xem vấn đề còn tồn tại ở đâu.
#define OPENMP_COMPAT
Đây là giai đoạn thứ hai. Khi bạn muốn bắt đầu tận dụng tất cả các tính năng cải tiến của open.mp như điều khiển thực thể chi tiết cho từng người chơi và loại bỏ giới hạn, bạn cần bắt đầu sử dụng các phiên bản mới của các chức năng. Các chức năng mới luôn khả dụng, nhưng bạn có thể không biết cần chuyển đổi ở đâu. Điều này sẽ đưa ra cảnh báo cho các chức năng cũ, nhưng chúng vẫn hoạt động, cho phép bạn chuyển đổi từng phần của chế độ của mình.
No define
Khi bạn nghĩ rằng mình đã hoàn tất việc chuyển đổi mã, hãy xóa các định nghĩa:
#include <openmp\openmp>
main() {}
Bây giờ bạn sẽ nhận được lỗi thay vì cảnh báo cho bất kỳ mã cũ nào vẫn đang được sử dụng.
New API
Bây giờ chúng ta đã thấy những vấn đề với API cũ và cách tìm ra nơi bạn cần áp dụng API mới, chúng ta thực sự nên xem xét API mới đó theo các vấn đề đã xác định trước đó:
Tags
Các hàm hiện sử dụng thẻ ở hầu hết mọi nơi. Chúng tôi đã cố gắng tìm sự cân bằng giữa quá nhiều cảnh báo thẻ và không đủ thông tin hữu ích, nhưng cảnh báo có lý do và có thể giúp tìm ra các vấn đề mà bạn có thể đã bỏ sót. Ví dụ: truyền một phương tiện làm tham số cho một hàm đối tượng hoặc cung cấp cho ai đó một vũ khí không tồn tại:
// No warnings for this code, despite it being clearly wrong.
new object = CreateObject(various, parameters, here);
PutPlayerInVehicle(playerid, object);
// Same here - there's no weapon 20, despite it being amongst valid weapon IDs.
GivePlayerWeapon(playerid, 20, 200);
Sẽ tốt hơn nhiều nếu tất cả các đoạn mã rõ ràng là sai có thể đưa ra cảnh báo. Đây là sức mạnh của ngôn ngữ an toàn kiểu, và trong khi pawn không hoàn toàn an toàn kiểu, chúng ta có thể tiến gần hơn với các thẻ. Những ví dụ đó trở thành:
// warning 213: tag mismatch: expected tag "Vehicle", but found "Object"
new Object:object = Object@Create(various, parameters, here);
Player_PutInVehicle(playerid, object);
// warning 213: tag mismatch: expected tag "WeaponType", but found but found none ("_")
GivePlayerWeapon(playerid, 20, 200);
// This doesn't give a warning:
GivePlayerWeapon(playerid, WEAPON_COLT45, 200);
Naming
Hầu hết các hàm mới đều sử dụng một cách tinh chỉnh của lược đồ đặt tên đã được nhiều thư viện và plugin áp dụng - Module_VerbNoun
. Một số thì không, khi chúng là một hàm riêng lẻ không phù hợp với bất kỳ mô-đun lớn nào, nhưng phần lớn bạn có thể đoán được tên của hàm mình cần. Không còn phải băn khoăn liệu đó là "Rot" hay "Rotation" cho loại phần tử này nữa, không có chữ viết tắt nào trừ khi tên hàm quá dài (32+ ký tự, trình biên dịch sẽ không chấp nhận chúng). Bạn muốn thay đổi mô hình của một đối tượng? Object_SetModel
. Bạn muốn hiển thị văn bản cho người chơi? Text_Show
.
Có một tập hợp động từ hạn chế - Get
, Set
, Create
, Destroy
, Add
, Remove
, Show
, Hide
, Run
, Move
, Stop
và Count
. Có thể thêm nhiều hơn và có thể xuất hiện trong những tình huống đặc biệt, nhưng nhìn chung nếu một trong những điều này phù hợp, thì có lẽ là điều đó. Phổ biến nhất là Get
và Set
, không giống như trong SA:MP hiện tại luôn đi theo cặp - nếu bạn có thể đặt bất kỳ tham số nào, bạn cũng có thể lấy nó sau. Chúng cũng là những động từ thường đi kèm với danh từ - bạn cần chỉ định những gì cần lấy hoặc đặt - Health
, Position
, Model
, Width
, v.v.
Một số ví dụ:
native bool:Menu_SetDisabled(Menu:menuid, bool:disabled);
native bool:Menu_GetDisabled(Menu:menuid);
native bool:Text_SetAlignment(Text:text, alignment);
native Text_GetAlignment(Text:text);
native bool:Object_Move(Object:objectid, Float:posX, Float:posY, Float:posZ, Float:speed, Float:rotX = FLOAT_NAN, Float:rotY = FLOAT_NAN, Float:rotZ = FLOAT_NAN);
native DBResult_CountRows(DBResult:dbresult);
native Player_Spawn(Player:playerid);
Lưu ý rằng tên mô-đun và thẻ luôn khớp - Vehicle
, DB
, Player
, v.v. Ngoài tính nhất quán, còn có lý do khác cho điều này - nó cung cấp giao diện giống OO hơn và dễ nhớ hơn. Tuy nhiên, bạn có thể đã nhận thấy trong một ví dụ trước đó hàm Object@Create
, không phải Object_Create
. Lý do cho điều này là những gì có trong tham số đầu tiên.
Trong tất cả bảy ví dụ ngay phía trên tham số đầu tiên là thực thể (đối tượng, phương tiện, người chơi, v.v.) đang được vận hành. Bạn muốn có vị trí của một thực thể cụ thể. Bạn muốn di chuyển một thực thể cụ thể. Bạn muốn có thời gian còn lại của một thực thể cụ thể. Điều này một lần nữa ánh xạ tới API giống OO - Player_Spawn(playerid)
có thể được coi là player.Spawn()
. PAWN không thể làm điều đó, nhưng điều đó không có nghĩa là các ngôn ngữ khác cắm vào API này không thể. Theo thuật ngữ C++ - _
là .
hoặc ->
và luôn cần một ID hợp lệ được đưa ra làm tham số đầu tiên. Tuy nhiên, hàm Dialog_IsValid(Dialog:id)
theo định nghĩa có thể không có ID hợp lệ làm tham số đầu tiên (hoặc mục đích của nó là gì*?), và Icon_Create(image, Float:x, Float:y, Float:z)
thậm chí không lấy ID nào cả. Do đó, chúng trở thành @
thay vào đó - ::
trong cú pháp C++. Chúng có thể không lấy ID nào cả và hoàn toàn không cần ID hợp lệ (@Destroy
cũng nằm trong nhóm hàm này, vì việc hủy một thực thể là một hoạt động logic bên ngoài một thực thể, không phải là một hoạt động trên thực thể).
Constants
Để bắt đầu, thay vì #define
cho mọi thứ, chúng ta sử dụng const
và enum
càng nhiều càng tốt, ngoại trừ những nơi chúng ta mong đợi mọi thứ sẽ bị ghi đè (MAX_PLAYERS
):
// Defining this first didn't previously work, now it does.
#define MAX_PLAYERS 100
#include <openmp\openmp>
// The override method still works as well.
#undef MAX_PLAYERS
#define MAX_PLAYERS 200
Sử dụng enum có nghĩa là hằng số có thẻ để kiểm tra thời gian biên dịch:
enum ObjectMaterialTextAlignment
{
MATERIAL_TEXT_ALIGN_LEFT,
MATERIAL_TEXT_ALIGN_CENTRE,
MATERIAL_TEXT_ALIGN_RIGHT,
};
Object_SetMaterialText
hiện chỉ chấp nhận một trong ba giá trị đó và không chấp nhận giá trị nào khác.
Còn ID không hợp lệ thì sao? Chúng tôi cũng đã làm cho chúng nhất quán. Tất cả các thực thể hiện sử dụng cùng một giá trị không hợp lệ, nằm ngoài phạm vi giá trị có thể hợp lệ - open.mp đã xóa hầu hết mọi giới hạn, do đó, việc làm cho một giá trị như 65536
không hợp lệ sẽ không hiệu quả. Giá trị không hợp lệ mới này là gì? Chúng tôi vẫn chưa quyết định... Có hai ứng cử viên chính đều có ưu và nhược điểm, và quyết định không dễ dàng như thoạt nhìn. Nhưng may mắn thay, nó không tạo ra sự khác biệt lớn đối với công việc nội bộ vì chúng tôi có thể chuyển đổi gần như ngay lập tức.
Hai lựa chọn được giải thích bên dưới và chúng tôi rất mong nhận được phản hồi về điều này nếu có thể:
0
Sử dụng 0
làm giá trị không hợp lệ có một số lợi thế:
nó không phải là chỉ mục không hợp lệ, do đó khi trả về và không được kiểm tra chính xác, mã của bạn sẽ không bị sập. Nó có thể không hoạt động hoàn hảo, nhưng ít nhất nó sẽ tiếp tục thực hiện một số việc.
Rất dễ kiểm tra và ý định sẽ trở nên rõ ràng:
new Object:object = Object@Create(various, parameters, here);
if (object)
{
Object_SetMaterialText(object, "Hello");
}
else
{
printf("Couldn't create the object.");
}
- Một biến mới được khai báo theo mặc định là một giá trị không hợp lệ:
new Dialog:d;
Một trong những lỗi phổ biến nhất mà mọi người gặp phải là mã của họ chỉ hoạt động với người chơi 0, vì họ quên khởi tạo một biến. Nếu không có người chơi/đối tượng/phương tiện 0, mã sẽ không áp dụng cho bất kỳ ai - tốt hơn là không có người chơi nào được thăng chức lên quản trị viên hơn là người chơi sai.
-1
Sử dụng -1
làm giá trị không hợp lệ có một số lợi thế:
Đây LÀ một chỉ mục không hợp lệ. Không phải là một chỉ mục được liệt kê là một lợi thế cho
0
vì mã của bạn thường sẽ tiếp tục chạy thay vì bị sập, nhưng với crashdetect thì đó có thể là một điều tốt - có một lỗi trong mã của bạn và sự cố sẽ cho bạn biết chính xác vị trí của nó, đôi khi là đến dòng mã nguồn chính xác. Cái nào tốt hơn, tiếp tục âm thầm hay kết thúc một cách ầm ĩ?Mọi người thường quen với
0
là một giá trị hợp lệ. Các lập trình viên bắt đầu đếm ở0
, vì vậy nó phải hợp lệ và một cái gì đó nằm ngoài phạm vi số nguyên dương phải không hợp lệ.Trong toán học không dấu, nó là số nguyên lớn nhất có thể -
0xFFFFFFFF
,4294967295
. Điều này có nghĩa là giới hạn cứng bên trong cho bất kỳ loại thực thể nào là giới hạn cao nhất có thể -4.294.967.295
mục trước khi hết ID (và bộ nhớ).
Per-Player Functions
Tóm lại, những thứ này không còn nữa. Mọi hàm ForPlayer
, ForAll
, CreatePlayerX
v.v. đều đã được thay thế bằng một hàm đơn giản - X_Display
(trong đó X
là bất kỳ thực thể nào) và X_Has
để kiểm tra:
Object_Display(objectid, playerid, true); // Show it.
Object_Display(objectid, playerid, false); // Hide it.
Text_Display(textid, true); // Show it to everyone.
if (Zone_Has(zoneid, playerid))
{
// The player is ALLOWED to see the gang zone.
}
YSI sử dụng X_SetPlayer
, nhưng việc hiển thị mọi thứ cho người chơi là điều cơ bản nhất cần làm, vì vậy nó xứng đáng có động từ riêng. Một số thư viện sử dụng X_Show
và X_Hide
, nhưng đó là hai hàm chỉ dẫn đến mã thừa khi kiểm tra những gì cần làm:
if (var) Checkpoint_Show(cpid, playerid);
else Checkpoint_Hide(cpid, playerid);
vs:
Checkpoint_Display(cpid, playerid, var);
Lưu ý rằng chỉ cần gọi X_Display
có thể không thực sự hiển thị mục. Một vật thể ở phía xa của thế giới vẫn sẽ không hiển thị. Một điểm kiểm tra trong một thế giới ảo khác sẽ không hiển thị ngay cả khi nó ở ngay cạnh bạn. Đối với các thực thể thế giới, điều này chỉ nói rằng người chơi được phép nhìn thấy nó, chứ không phải là họ hiện có thể nhìn thấy. Ngược lại, đối với các thành phần HUD như menu và hộp thoại, điều này sẽ hiển thị chúng ngay lập tức và có thể ẩn các thành phần khác khi chỉ được phép nhìn thấy một thành phần.
IDs
Với việc loại bỏ nhóm người chơi và thống nhất ID không hợp lệ, đây không còn là vấn đề nữa.
Smarter functions.
The X_Display
functions shown above can take two parameters - entity and display state, to enable every player to see them; or alternatively three parameters - entity, player, and display state. There are other functions that are smart about their parameters as well. One set of examples is the various rotation functions. As mentioned in the introduction, there are at least four different ways to get and set rotations on for different entities. Now there's one - X_SetRotation
and X_GetRotation
. For example:
// Just get `z`.
z = Player_GetRotation(playerid);
// Get x, y, and z Euler angles.
Object_GetRotation(objectid, x, y, z);
// Get w, x, y, and z quaternion angles.
Vehicle_GetRotation(vehicleid, w, x, y, z);
Cái nào được sử dụng cho thực thể nào? Tất cả chúng:
// Just get `z`.
z = Object_GetRotation(objectid);
// Get x, y, and z Euler angles.
Object_GetRotation(objectid, x, y, z);
// Get w, x, y, and z quaternion angles.
Object_GetRotation(objectid, w, x, y, z);
Ý nghĩa của tham số và giá trị trả về được xác định bởi số lượng tham số được cung cấp. Cũng như đối với Set
:
// Just set `z`.
Vehicle_SetRotation(vehicleid, z);
// Set x, y, and z Euler angles.
Vehicle_SetRotation(vehicleid, x, y, z);
// Set w, x, y, and z quaternion angles.
Vehicle_SetRotation(vehicleid, w, x, y, z);
Conclusion
Chúng tôi đã rất cố gắng để tạo ra một API dễ sử dụng, dễ học, nhưng cũng tương thích ngược. Phần lớn thành công của SA:MP đến từ tính dễ sử dụng và chúng tôi muốn duy trì tính dễ sử dụng đó, nhưng cũng nhận thức được rằng cũng có những người dùng thành thạo muốn khai thác nhiều hơn từ mã của họ. Đạt được sự cân bằng này luôn khó khăn, đặc biệt là khi những người bình luận nhiều nhất lại là những người có nhiều kinh nghiệm nhất - những người hiểu rõ ngôn ngữ và muốn phát triển hơn nữa. Điều này tạo ra một hệ thống tự chọn các tính năng nâng cao với cái giá phải trả là người mới bắt đầu. Chúng tôi không muốn điều này, nhưng chúng tôi vẫn muốn lắng nghe suy nghĩ của bạn. Bạn thích ngôn ngữ và tính năng API nào, bạn không thích tính năng nào? Những chức năng nào sẽ giúp bạn khai thác tối đa mã của mình? Bạn nghĩ thiết kế mới đơn giản hay quá phức tạp? Bạn có ổn với tên gọi ngẫu nhiên của các chức năng hiện tại không? Chúng phục vụ mục đích của chúng, vậy tại sao phải thay đổi chúng? Là người mới bắt đầu, bạn có đánh giá cao bất kỳ điều gì được thực hiện khác đi không?
Vui lòng chia sẻ bất kỳ phản hồi nào bạn có thể có ở đây trong chủ đề burgershot bên dưới. Chúng tôi rất mong nhận được phản hồi từ bạn:
https://forum.open.mp/showthread.php?tid=1036
* Ghi chú bên lề thú vị. Nhờ cách chúng tôi tóm tắt mã API tập lệnh, Dialog_IsValid
được triển khai như sau:
SCRIPT_API(Dialog_IsValid, bool(Dialog_s)) { return true; }
Chính xác là như vậy. Không cần triển khai thực tế vì để hàm được gọi, tra cứu thực thể phải thành công và do đó chúng ta có thể trả về true
ngay lập tức.