OnPlayerKeyStateChange
توضیحات
این کالبک زمانی فراخوانده میشود که وضعیت هر کلید پشتیبانی شده تغییر کند (فشرده/رها شده).
کلیدهای جهتی OnPlayerKeyStateChange را فعال نمیکنند (بالا/پایین/چپ/راست).
نام | توضیحات |
---|---|
playerid | شناسه بازیکنی که کلید را فشرده یا رها کرده. |
KEY:newkeys | نقشه (bitmask) کلیدهای فعلاً نگه داشته شده - اینجا ببینید |
KEY:oldkeys | نقشه (bitmask) کلیدهای نگه داشته شده قبل از تغییر فعلی - اینجا ببینید. |
مقادیر برگشتی
- این کالبک برگشتها را مدیریت نمیکند.
- همیشه ابتدا در gamemode فراخوانده میشود.
نکتهها
این کالبک همچنین میتواند توسط NPC فراخوانده شود.
کلیدهای جهتی OnPlayerKeyStateChange را فعال نمیکنند (بالا/پایین/چپ/راست).
آنها فقط میتوانند با GetPlayerKeys (در OnPlayerUpdate یا تایمر) تشخیص داده شوند.
توابع مرتبط
توابع زیر ممکن است مفید باشند، زیرا به نوعی با این کالبک در ارتباط هستند.
- GetPlayerKeys: بررسی اینکه بازیکن چه کلیدهایی را نگه داشته.
اطلاعات اضافی
مقدمه
این کالبک هر زمان که بازیکن یکی از کلیدهای پشتیبانی شده را فشار دهد یا رها کند فراخوانده میشود (کلیدها را ببینید).
کلیدهایی که پشتیبانی میشوند کلیدهای واقعی صفحه کلید نیستند، بلکه کلیدهای تابعی نقشهبرداری شده San Andreas هستند، این به این معناست که، برای مثال، نمیتوانید تشخیص دهید که کسی spacebar را فشرده، اما میتوانید تشخیص دهید که کلید دویدن خود را فشرده (که ممکن است، یا ممکن است نباشد، به spacebar اختصاص داده شده باشد (به طور پیشفرض هست)).
پارامترها
پارامترهای این تابع لیستی از تمام کلیدهای فعلاً نگه داشته شده و تمام کلیدهای نگه داشته شده لحظهای قبل هستند. کالبک زمانی فراخوانده میشود که وضعیت کلید تغییر کند (یعنی وقتی کلید فشرده یا رها شود) و وضعیتهای تمام کلیدها قبل و بعد از این تغییر را ارسال میکند. این اطلاعات میتوانند برای تشخیص دقیق آنچه اتفاق افتاده استفاده شوند اما متغیرها نمیتوانند مستقیماً به همان شکل پارامترهای توابع دیگر استفاده شوند. برای کاهش تعداد متغیرها فقط یک BIT برای نمایندگی یک کلید استفاده میشود، این به این معناست که یک متغیر ممکن است چندین کلید را همزمان شامل شود و مقایسه ساده مقادیر همیشه کار نخواهد کرد.
چگونه کلید را بررسی نکنیم
فرض کنیم میخواهید تشخیص دهید که بازیکن دکمه FIRE خود را فشرده، کد آشکار این خواهد بود:
if (newkeys == KEY_FIRE)
این کد ممکن است حتی در تستهای شما کار کند، اما اشتباه است و تستهای شما کافی نیستند. سعی کنید خم شوید و fire را بزنید - کد شما فوراً کار نخواهد کرد. چرا؟ زیرا "newkeys" دیگر مشابه "KEY_FIRE" نیست، مشابه "KEY_FIRE" ترکیب شده با "KEY_CROUCH" است.
چگونه کلید را بررسی کنیم
پس، اگر متغیر میتواند چندین کلید را همزمان شامل شود، چگونه فقط یکی را بررسی میکنید؟ پاسخ bit masking است. هر کلید bit خود را در متغیر دارد (برخی کلیدها همان bit را دارند، اما آنها کلیدهای onfoot/incar هستند، بنابراین نمیتوانند همزمان فشرده شوند) و شما باید فقط همان bit واحد را بررسی کنید:
if (newkeys & KEY_FIRE)
توجه کنید که تک & درست است - این یک bitwise AND است، نه logical AND که دو ampersand نامیده میشود.
حالا اگر این کد را تست کنید، چه خم باشید چه ایستاده وقتی کلید fire را میزنید کار خواهد کرد. اما هنوز یک مشکل کوچک وجود دارد - تا زمانی که کلید را نگه دارید کار خواهد کرد. OnPlayerKeyStateChange هر بار که کلید تغییر کند فراخوانده میشود و آن کد هر زمان که کلید fire نگه داشته شود درست است. اگر fire را بزنید کد کار خواهد کرد، اگر آن کلید نگه داشته شود و crouch را بزنید - آن کد دوباره کار خواهد کرد زیرا کلیدی (crouch) تغییر کرده و fire هنوز نگه داشته شده است. چگونه تشخیص دهید که کلید ابتدا فشرده شده، اما دوباره فعال نشود وقتی هنوز نگه داشته شده و کلید دیگری تغییر میکند؟
چگونه بررسی کنیم که کلید فشرده شده
اینجاست که "oldkeys" وارد میشود. برای بررسی اینکه کلید تازه فشرده شده، ابتدا باید بررسی کنید که در "newkeys" تنظیم شده - یعنی نگه داشته شده، و سپس بررسی کنید که در "oldkeys" نیست - یعنی تازه نگه داشته شده. کد زیر این کار را انجام میدهد:
if ((newkeys & KEY_FIRE) && !(oldkeys & KEY_FIRE))
این فقط زمانی درست خواهد بود که کلید FIRE ابتدا فشرده شود، نه زمانی که نگه داشته شود و کلید دیگری تغییر کند.
چگونه بررسی کنیم که کلید رها شده
دقیقاً همان اصل بالا، اما برعکس:
if ((oldkeys & KEY_FIRE) && !(newkeys & KEY_FIRE))
چگونه چندین کلید را بررسی کنیم
اگر میخواهید بازیکنان را که crouch و fire را نگه داشتهاند بررسی کنید، کد زیر خوب کار خواهد کرد:
if ((newkeys & KEY_FIRE) && (newkeys & KEY_CROUCH))
اما اگر میخواهید تشخیص دهید که آنها ابتدا fire و crouch را فشردند، کد زیر کار نخواهد کرد. اگر موفق شوند دو کلید را دقیقاً همزمان فشار دهند کار خواهد کرد، اما اگر جزئی خارج باشند (بسیار کمتر از نیم ثانیه) کار نخواهد کرد:
if ((newkeys & KEY_FIRE) && !(oldkeys & KEY_FIRE) && (newkeys & KEY_CROUCH) && !(oldkeys & KEY_CROUCH))
چرا نه؟ زیرا OnPlayerKeyStateChange هر بار که کلید واحد تغییر کند فراخوانده میشود. پس آنها "KEY_FIRE" را میزنند - OnPlayerKeyStateChange با "KEY_FIRE" در "newkeys" و نه در "oldkeys" فراخوانده میشود، سپس آنها "KEY_CROUCH" را میزنند - OnPlayerKeyStateChange با "KEY_CROUCH" و "KEY_FIRE" در "newkeys" فراخوانده میشود، اما "KEY_FIRE" اکنون در "oldkeys" نیز هست زیرا قبلاً فشرده شده، پس "!(oldkeys & KEY_FIRE)" ناموفق خواهد بود. خوشبختانه راهحل بسیار ساده است (در واقع سادهتر از کد اصلی):
if ((newkeys & (KEY_FIRE | KEY_CROUCH)) == (KEY_FIRE | KEY_CROUCH) && (oldkeys & (KEY_FIRE | KEY_CROUCH)) != (KEY_FIRE | KEY_CROUCH))
این ممکن است پیچیده به نظر برسد، اما بررسی میکند که هر دو کلید در "newkeys" تنظیم شدهاند و اینکه هر دو کلید در "oldkeys" تنظیم نشدهاند، اگر یکی از آنها در "oldkeys" تنظیم شده بود مهم نیست زیرا هر دو نبودهاند. تمام این چیزها میتوانند با define ها بسیار ساده شوند.
سادهسازی
تشخیص نگه داشتن کلید
define:
// HOLDING(keys)
#define HOLDING(%0) \
((newkeys & (%0)) == (%0))
نگه داشتن یک کلید:
if (HOLDING( KEY_FIRE ))
نگه داشتن چندین کلید:
if (HOLDING( KEY_FIRE | KEY_CROUCH ))
تشخیص اولین بار فشردن کلید
define:
// PRESSED(keys)
#define PRESSED(%0) \
(((newkeys & (%0)) == (%0)) && ((oldkeys & (%0)) != (%0)))
فشردن یک کلید:
if (PRESSED( KEY_FIRE ))
فشردن چندین کلید:
if (PRESSED( KEY_FIRE | KEY_CROUCH ))
تشخیص اینکه بازیکن در حال حاضر کلید را فشرده
define:
// PRESSING(keyVariable, keys)
#define PRESSING(%0,%1) \
(%0 & (%1))
فشردن یک کلید:
if (PRESSING( newkeys, KEY_FIRE ))
فشردن چندین کلید:
if (PRESSING( newkeys, KEY_FIRE | KEY_CROUCH ))
تشخیص رها کردن کلید
define:
// RELEASED(keys)
#define RELEASED(%0) \
(((newkeys & (%0)) != (%0)) && ((oldkeys & (%0)) == (%0)))
رها کردن یک کلید:
if (RELEASED( KEY_FIRE ))
رها کردن چندین کلید:
if (RELEASED( KEY_FIRE | KEY_CROUCH ))
مثالها
وصل کردن NOS وقتی بازیکن fire را میزند
public OnPlayerKeyStateChange(playerid, KEY:newkeys, KEY:oldkeys)
{
if (PRESSED(KEY_FIRE))
{
if (IsPlayerInAnyVehicle(playerid))
{
AddVehicleComponent(GetPlayerVehicleID(playerid), 1010);
}
}
return 1;
}
پرش فوقالعاده
public OnPlayerKeyStateChange(playerid, KEY:newkeys, KEY:oldkeys)
{
if (PRESSED(KEY_JUMP))
{
new
Float:x,
Float:y,
Float:z;
GetPlayerPos(playerid, x, y, z);
SetPlayerPos(playerid, x, y, z + 10.0);
}
return 1;
}
حالت خدا در حین نگه داشتن use
new
Float:gPlayerHealth[MAX_PLAYERS];
#if !defined INFINITY
#define INFINITY (Float:0x7F800000)
#endif
public OnPlayerKeyStateChange(playerid, KEY:newkeys, KEY:oldkeys)
{
if (PRESSED(KEY_ACTION))
{
// They just pressed the action key, save their
// old health for restoration.
GetPlayerHealth(playerid, gPlayerHealth[playerid]);
SetPlayerHealth(playerid, INFINITY);
}
else if (RELEASED(KEY_ACTION))
{
// They just let go of action - restore
// their old health again.
SetPlayerHealth(playerid, gPlayerHealth[playerid]);
}
return 1;
}
توضیح
شما نیازی نیست نگران چگونگی انجام آن باشید، فقط اینکه انجام میشود. HOLDING تشخیص میدهد که آنها کلید (یا کلیدها) را فشار میدهند، صرفنظر از اینکه قبلاً آن را فشار میدادند یا نه، PRESSED تشخیص میدهد که آنها تازه کلید(ها) را فشردند و RELEASED تشخیص میدهد که آنها تازه کلید(ها) را رها کردند. اما اگر میخواهید بیشتر بدانید - ادامه دهید.
دلیل اینکه باید به این شکل انجام دهید، نه فقط با استفاده از & یا ==، تشخیص دقیق کلیدهایی که میخواهید است در حالی که دیگران را که ممکن است فشرده شده یا نشده باشند نادیده میگیرید. در باینری KEY_SPRINT این است:
0b00001000
و KEY_JUMP این است:
0b00100000
بنابراین OR کردن آنها در کلیدهای مورد نظر (همچنین میتوانستیم آنها را در این مثال جمع کنیم اما همیشه اینطور نیست) به ما میدهد:
0b00101000
اگر فقط از & استفاده میکردیم و OnPlayerKeyStateChange برای بازیکنی که jump را میزند فراخوانی میشد، کد زیر را دریافت میکردیم:
newkeys = 0b00100000
wanted = 0b00101000
ANDed = 0b00100000
AND دو عدد صفر نیست، بنابراین نتیجه بررسی درست است، که چیزی نیست که ما میخواهیم.
اگر فقط از == استفاده کردیم دو عدد واضحاً یکسان نیستند بنابراین بررسی ناموفق خواهد بود، که چیزی است که میخواهیم.
اگر بازیکن jump، sprint و crouch را فشار میداد، کد زیر را دریافت میکردیم:
newkeys = 0b00101010
wanted = 0b00101000
ANDed = 0b00101000
نسخه ANDed همان کلیدهای مورد نیاز است و همچنین صفر نیست، بنابراین پاسخ درست خواهد داد، اما دو عدد اصلی یکسان نیستند پس == ناموفق خواهد بود. در هر دو مثال یکی از دو پاسخ درست و یکی پاسخ اشتباه داده است. اگر اولی را با استفاده از & و == مقایسه کنیم:
newkeys = 0b00100000
wanted = 0b00101000
ANDed = 0b00100000
واضح است که wanted و ANDed یکسان نیستند پس بررسی ناموفق میشود، که درست است. برای مثال دوم:
newkeys = 0b00101010
wanted = 0b00101000
ANDed = 0b00101000
Wanted و ANDed یکسان هستند پس مقایسه آنها به عنوان مساوی نتیجهای درست خواهد داد، که دوباره درست است.
پس با استفاده از این روش میتوانیم به دقت بررسی کنیم که آیا کلیدهای خاصی فشرده شدهاند یا نه، تمام کلیدهای دیگر که ممکن است فشرده شده یا نشده باشند را نادیده میگیریم. بررسی oldkeys فقط از != به جای == استفاده میکند تا اطمینان حاصل کند که کلیدهای مورد نیاز قبلاً فشرده نشدهاند، پس میدانیم یکی از آنها تازه فشرده شده.