ইভেন্ট-চালিত সিস্টেমে নেস্টেড ইনপুট


11

আমি ইভেন্ট এবং প্রতিনিধিদের সাথে ইভেন্ট-ভিত্তিক ইনপুট হ্যান্ডলিং সিস্টেমটি ব্যবহার করছি। একটি উদাহরণ:

InputHander.AddEvent(Keys.LeftArrow, player.MoveLeft); //Very simplified code

যাইহোক, আমি কীভাবে 'নেস্টেড' ইনপুটটি মোকাবেলা করব তা নিয়ে ভাবতে শুরু করি। উদাহরণস্বরূপ অর্ধ-জীবন 2 (বা কোনও উত্স গেম, সত্যিই), আপনি এতে আইটেম বাছাই করতে পারেন E। আপনি যখন কোনও জিনিস তুলেছেন, তখন আপনি গুলি চালাতে পারবেন না Left Mouse, তবে পরিবর্তে আপনি বস্তুটি ফেলে দিন throw আপনি এখনও সঙ্গে লাফ দিতে পারেন Space

(আমি বলছি নেস্টেড ইনপুটটি যেখানে আপনি একটি নির্দিষ্ট কী টিপেন এবং আপনি যে ক্রিয়াগুলি করতে পারেন তা পরিবর্তন করতে পারেন a মেনু নয়))

তিনটি মামলা হ'ল:

  • আগের মতো একই ক্রিয়া করতে সক্ষম হওয়া (ঝাঁপ দেওয়ার মতো)
  • একই ক্রিয়া করতে সক্ষম হচ্ছেন না (গুলি চালানোর মতো)
  • সম্পূর্ণ আলাদাভাবে একটি ক্রিয়া করা (যেমন নেটহ্যাকের মতো যেখানে ওপেন ডোর কীটি চাপার অর্থ আপনি সরে যাচ্ছেন না, তবে দরজাটি খোলার জন্য একটি দিক নির্বাচন করুন)

আমার আসল ধারণাটি ছিল ইনপুট পাওয়ার পরে এটি পরিবর্তন করা:

Register input 'A' to function 'Activate Secret Cloak Mode'

In 'Secret Cloak Mode' function:
Unregister input 'Fire'
Unregister input 'Sprint'
...
Register input 'Uncloak'
...

এটি প্রচুর পরিমাণে সংযোজন, পুনরাবৃত্তি কোড এবং অন্যান্য খারাপ নকশার লক্ষণগুলি ভোগ করে।

আমার ধারণা অন্যান্য বিকল্পটি হ'ল এক ধরণের ইনপুট স্টেট সিস্টেম বজায় রাখা - সম্ভবত এমন অনেক নিবন্ধক / নিবন্ধককে একটি পরিষ্কার জায়গায় (ইনপুট সিস্টেমে কোনও ধরণের স্ট্যাক সহ) রিফ্যাক্টর করার জন্য বা অন্য কোনও ডেলিগেট সম্ভবত রেজিস্টার ফাংশনটিতে থাকা অন্য কোনও প্রতিনিধি বা সম্ভবত কোন অ্যারে রয়েছে? রাখা এবং কি না।

আমি নিশ্চিত যে এখানে কেউ না কেউ অবশ্যই এই সমস্যার মুখোমুখি হয়েছেন। কীভাবে সমাধান করেছেন?

tl; dr আমি কোনও ইভেন্ট সিস্টেমে অন্য নির্দিষ্ট ইনপুট পরে প্রাপ্ত নির্দিষ্ট ইনপুটটির সাথে কীভাবে ডিল করব?

উত্তর:


7

দুটি বিকল্প: যদি "নেস্টেড ইনপুট" ক্ষেত্রে সর্বাধিক তিন, চার হয় তবে আমি কেবল পতাকা ব্যবহার করব। "কোন জিনিস ধরে আছে? গুলি চালানো যায় না?" অন্য যে কোনও কিছু এটির উপরে নজর রাখছে।

অন্যথায়, আপনি ইভেন্ট হ্যান্ডলারগুলির প্রতি ইনপুট-কী স্ট্যাক রাখতে পারেন।

Actions.Empty = () => { return; };
if(IsPressed(Keys.E)) {
    keyEventHandlers[Keys.E].Push(Actions.Empty);
    keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
    keyEventHandlers[Keys.Space].Push(Actions.Empty);
} else if (IsReleased(Keys.E)) {
    keyEventHandlers[Keys.E].Pop();
    keyEventHandlers[Keys.LeftMouseButton].Pop();
    keyEventHandlers[Keys.Space].Pop();        
}

while(GetNextKeyInBuffer(out key)) {
   keyEventHandlers[key].Invoke(); // we invoke only last event handler
}

বা এই প্রভাব কিছু :)

সম্পাদন করা : কেউ যদি অন্য নির্মাণগুলি পরিচালনা না করে তবেই তাকে উল্লেখযোগ্য। আমরা কি কোনও ইনপুট ইভেন্ট হ্যান্ডলিং রুটিনের জন্য পুরো ডেটা-চালিত যেতে যাচ্ছি? আপনি অবশ্যই করতে পারেন, কিন্তু কেন?

যাইহোক, এটি হ্যাকের জন্য:

void BuildOnKeyPressedEventHandlerTable() {
    onKeyPressedHandlers[Key.E] = () => { 
        keyEventHandlers[Keys.E].Push(Actions.Empty);
        keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
        keyEventHandlers[Keys.Space].Push(Actions.Empty);
    };
}

void BuildOnKeyReleasedEventHandlerTable() {
    onKeyReleasedHandlers[Key.E] = () => { 
        keyEventHandlers[Keys.E].Pop();
        keyEventHandlers[Keys.LeftMouseButton].Pop();
        keyEventHandlers[Keys.Space].Pop();              
    };
}

/* get released keys */

foreach(var releasedKey in releasedKeys)
    onKeyReleasedHandlers[releasedKey].Invoke();

/* get pressed keys */
foreach(var pressedKey in pressedKeys) 
    onKeyPressedHandlers[pressedKey].Invoke();

keyEventHandlers[key].Invoke(); // we invoke only last event handler

সম্পাদনা 2

কাইলোটান কী ম্যাপিংয়ের কথা উল্লেখ করেছে, যা প্রতিটি গেমের হওয়া উচিত এমন একটি প্রাথমিক বৈশিষ্ট্য (অ্যাক্সেসযোগ্যতার বিষয়েও ভাবুন)। কীম্যাপিং সহ একটি আলাদা গল্প।

একটি মূল প্রেস সংমিশ্রণ বা ক্রমের উপর নির্ভর করে আচরণ পরিবর্তন করা সীমাবদ্ধ। আমি সেই অংশটিকে অবহেলা করেছি।

আচরণ খেলা যুক্তি সম্পর্কিত এবং ইনপুট নয় is যা মোটামুটি সুস্পষ্ট, এটি ভাবতে আসে।

অতএব, আমি নিম্নলিখিত সমাধান প্রস্তাব করছি:

// //>

void Init() {
    // from config file / UI
    // -something events should be set automatically
    // quake 1 ftw.
    // name      family         key      keystate
    "+forward" "movement"   Keys.UpArrow Pressed
    "-forward"              Keys.UpArrow Released
    "+shoot"   "action"     Keys.LMB     Pressed
    "-shoot"                Keys.LMB     Released
    "jump"     "movement"   Keys.Space   Pressed
    "+lstrafe" "movement"   Keys.A       Pressed
    "-lstrafe"              Keys.A       Released
    "cast"     "action"     Keys.RMB     Pressed
    "picknose" "action"     Keys.X       Pressed
    "lockpick" "action"     Keys.G       Pressed
    "+crouch"  "movement"   Keys.LShift  Pressed
    "-crouch"               Keys.LShift  Released
    "chat"     "user"       Keys.T       Pressed      
}  

void ProcessInput() {
    var pk = GetPressedKeys();
    var rk = GetReleasedKeys();

    var actions = TranslateToActions(pk, rk);
    PerformActions(actions);
}                

void TranslateToActions(pk, rk) {
    // use what I posted above to switch actions depending 
    // on which keys have been pressed
    // it's all about pushing and popping the right action 
    // depending on the "context" (it becomes a contextual action then)
}

actionHandlers["movement"] = (action, actionFamily) => {
    if(player.isCasting)
        InterruptCast();    
};

actionHandlers["cast"] = (action, actionFamily) => {
    if(player.isSilenced) {
        Message("Cannot do that when silenced.");
    }
};

actionHandlers["picknose"] = (action, actionFamily) => {
    if(!player.canPickNose) {
        Message("Your avatar does not agree.");
    }
};

actionHandlers["chat"] = (action, actionFamily) => {
    if(player.isSilenced) {
        Message("Cannot chat when silenced!");
    }
};

actionHandlers["jump"] = (action, actionFamily) => {
    if(player.canJump && !player.isJumping)
        player.PerformJump();

    if(player.isJumping) {
        if(player.CanDoubleJump())
            player.PerformDoubleJump();
    }

    player.canPickNose = false; // it's dangerous while jumping
};

void PerformActions(IList<ActionEntry> actions) {
    foreach(var action in actions) {
        // we pass both action name and family
        // if we find no action handler, we look for an "action family" handler
        // otherwise call an empty delegate
        actionHandlers[action.Name, action.Family]();    
    }
}

// //<

আমার চেয়ে স্মার্ট লোকেরা এটি অনেক উপায়ে উন্নত করতে পারে, তবে আমি বিশ্বাস করি এটি একটি ভাল সূচনা পয়েন্টও।


সাধারণ গেমগুলির জন্য দুর্দান্ত কাজ করে - এখন আপনি কীভাবে এই কী-ম্যাপার কোড করবেন? :) আপনার ইনপুটটির জন্য ডেটা চালিত হওয়ার পক্ষে এটি একমাত্র যথেষ্ট কারণ হতে পারে।
কাইলোটন

কাইলোটান, এটি সত্যিই ভাল পর্যবেক্ষণ, আমি আমার উত্তরটি সম্পাদনা করতে যাচ্ছি।
রাইন

এটি একটি দুর্দান্ত উত্তর। এখানে, অনুগ্রহ করুন: পি
কম্যুনিস্ট হাঁস

@ কম্যুনিস্ট হাঁস - ধন্যবাদ চ্যাপ, আশা করি এটি সাহায্য করবে।
রাইন

11

আমরা পূর্বে উল্লিখিত হিসাবে আমরা একটি রাষ্ট্র ব্যবস্থা ব্যবহার করেছি।

আমরা একটি মানচিত্র তৈরি করব যাতে একটি পতাকা সহ একটি নির্দিষ্ট রাজ্যের সমস্ত কী থাকবে যা পূর্বে ম্যাপ করা কীগুলি না পেরিয়ে যেতে দেবে। যখন আমরা রাষ্ট্রগুলি পরিবর্তন করি তখন নতুন মানচিত্রটি চাপ দেওয়া হবে বা কোনও পূর্ববর্তী মানচিত্রটি পপআপ হয়ে যাবে।

ইনপুট রাজ্যের দ্রুত সরল উদাহরণ হ'ল ডিফল্ট, ইন-মেনু এবং ম্যাজিক-মোড। ডিফল্ট হ'ল যেখানে আপনি চারপাশে দৌড়াচ্ছেন এবং গেমটি খেলছেন। মেনুটি আপনি যখন স্টার্ট মেনুতে থাকবেন বা যখন আপনি কোনও শপ মেনু, বিরতি মেনু, একটি বিকল্প পর্দা খুলবেন তখনই হবে। ইন-মেনুতে পতাকা দিয়ে কোনও পাস থাকবে না কারণ আপনি একটি মেনু নেভিগেট করার সময় আপনি চান না যে আপনার চরিত্রটি চারদিকে ঘুরছে। অন্যদিকে, আইটেমটি বহন করার সাথে আপনার উদাহরণের মতো, ম্যাজিক-মোডটি সহজেই ক্রিয়া / আইটেম ব্যবহারের কীগুলি পরিবর্তে মন্ত্রটি কাস্ট করার জন্য পুনরায় তৈরি করবে (আমরা শব্দটি এবং কণার প্রভাবগুলির সাথে এটিও বেঁধে ফেলব তবে এটি কিছুটা অতিক্রম করে) তোমার প্রশ্ন).

কী কী কারণে মানচিত্রগুলি ধাক্কা দেওয়া এবং পপ করা যায় তা আপনার উপর নির্ভর করে এবং আমি এও সত্যই বলব যে মানচিত্রের স্ট্যাকটি পরিষ্কার রাখা হয়েছে, স্তরের লোডিং সবচেয়ে সুস্পষ্ট সময় হওয়ার বিষয়টি নিশ্চিত করার জন্য আমাদের কিছু 'স্পষ্ট' ঘটনা ছিল। বার)।

আশাকরি এটা সাহায্য করবে.

TL; DR - এমন রাজ্য এবং একটি ইনপুট মানচিত্র ব্যবহার করুন যা আপনি চাপতে এবং / অথবা পপ করতে পারেন। মানচিত্রটি পূর্ববর্তী ইনপুটটিকে পুরোপুরি সরিয়ে দেয় কি না তা বলার জন্য একটি পতাকা অন্তর্ভুক্ত করুন।


5
এই. বিবৃতি শয়তান হলে নেস্টেড পৃষ্ঠাগুলি এবং পৃষ্ঠাগুলি।
michael.bartnett

+1 টি। আমি যখন ইনপুটটিতে চিন্তা করি তখন আমার সর্বদা অ্যাক্রোব্যাট রিডার থাকে - টুল, হ্যান্ড টুল, মার্কি জুম নির্বাচন করুন। আইএমও, স্ট্যাক ব্যবহার করা অনেক সময় ওভারকিল হতে পারে। জিইএফ এটি অ্যাবস্ট্রাক্টুলের মাধ্যমে লুকায় । JHotDraw এর সরঞ্জাম প্রয়োগের একটি দুর্দান্ত শ্রেণিবিন্যাসের দৃশ্য রয়েছে ।
স্টিফান হানকে

2

এটি দেখে মনে হচ্ছে উত্তরাধিকার আপনার সমস্যার সমাধান করতে পারে। ডিফল্ট আচরণ প্রয়োগ করে এমন একটি গুচ্ছ পদ্ধতির সাথে আপনার একটি বেস ক্লাস থাকতে পারে। তারপরে আপনি এই শ্রেণিটি প্রসারিত করতে এবং কিছু পদ্ধতিতে ওভাররাইড করতে পারেন। স্যুইচিং মোড তখন বর্তমান বাস্তবায়ন স্যুইচ করার বিষয় মাত্র।

এখানে কিছু সিডো-কোড দেওয়া আছে

class DefaultMode
    function handle(key) {/* call the right method based on the given key. */}
    function run() { ... }
    function pickup() { ... }
    function fire() { ... }


class CarryingMode extends DefaultMode
      function pickup() {} //empty method, so no pickup action in this mode
      function fire() { /*throw object and switch to DefaultMode. */ }

এটি জেমস প্রস্তাবিত অনুরূপ।


0

আমি কোনও নির্দিষ্ট ভাষায় সঠিক কোড লিখছি না। আমি আপনাকে ধারণা দিচ্ছি।

1) আপনার ইভেন্টগুলিতে আপনার মূল ক্রিয়াগুলি ম্যাপ করুন।

(কী। লেফটমাউসবাটন, বাম_ক্লিক_সামগ্রী), (কীগুলি

2) নীচের মত আপনার ইভেন্টগুলি নিয়োগ / সংশোধন করুন

def left_click_event = fire();
def e_key_event = pick_item();
def space_key_event = jump();

pick_item() {
 .....
 left_click_action = throw_object();
}

throw_object() {
 ....
 left_click_action = fire();
}

fire() {
 ....
}

jump() {
 ....
}

আপনার লাফের ক্রিয়াটি লাফানো এবং আগুনের মতো অন্যান্য ইভেন্টের সাথে ডাকাডল থাকতে দেয়।

যদি অন্যথায় .. শর্তসাপেক্ষ চেকগুলি এড়িয়ে চলুন কারণ এটি নিয়ন্ত্রণহীন কোডের দিকে নিয়ে যায়।


এটি মোটেও সহায়তা করে বলে মনে হচ্ছে না। এটির উচ্চ স্তরের সংযোগের সমস্যা রয়েছে এবং এটি পুনরাবৃত্ত কোড বলে মনে হচ্ছে।
কমিউনিস্ট হাঁস

কেবল আরও ভাল বোঝার জন্য - আপনি কী এটি ব্যাখ্যা করতে পারবেন এতে "পুনরাবৃত্তিমূলক" কী এবং আপনি কোথায় "উচ্চ স্তরের সংযোগ" দেখছেন।
ইনরাজোর

আপনি যদি আমার কোড উদাহরণটি দেখতে পান তবে আমাকে নিজেই ফাংশনের সমস্ত ক্রিয়া সরিয়ে ফেলতে হবে। আমি নিজেই এগুলি ফাংশনে হার্ডকডিং করছি - যদি দুটি ফাংশন একই রেজিস্টার / নিবন্ধনগুলি ভাগ করতে চায় তবে কী হবে? আমার হয় হয় কোড নকল করতে হবে বা তাদের দুটি করতে হবে। এছাড়াও সমস্ত অযাচিত ক্রিয়াকলাপ সরিয়ে দেওয়ার জন্য প্রচুর পরিমাণে কোড থাকবে। সবশেষে, আমার এমন কিছু জায়গা থাকতে হবে যা সেগুলি প্রতিস্থাপনের জন্য সমস্ত মূল ক্রিয়াকে 'মনে রাখে'।
কমিউনিস্ট হাঁস

সম্পূর্ণ রেজিস্ট্রার / নিবন্ধীকরণের বিবৃতি সহ আপনি কি আমাকে আপনার কোড থেকে সত্য গোপনীয়করণের (গোপন চাদর ফাংশন) দুটি দিতে পারেন?
inRazor

আমার কাছে এই মুহুর্তে এই ক্রিয়াগুলির জন্য আসলে কোড নেই। তবে, secret cloakনিবন্ধভুক্ত হওয়ার জন্য আগুন, স্প্রিন্ট, হাঁটাচলা, এবং অস্ত্র পরিবর্তনের মতো জিনিসগুলির প্রয়োজন হবে এবং নিবন্ধভুক্ত করা হবে।
কমিউনিস্ট হাঁস

0

নিবন্ধভুক্ত না করে কেবল একটি রাষ্ট্র তৈরি করুন এবং তারপরে পুনরায় নিবন্ধন করুন।

In 'Secret Cloak Mode' function:
Grab the state of all bound keys- save somewhere
Unbind all keys
Re-register the keys/etc that we want to do.

In `Unsecret Cloak Mode`:
Unbind all keys
Rebind the state that we saved earlier.

অবশ্যই, এই সহজ ধারণা ব্যাপ্ত হবে আপনি চলন্ত জন্য পৃথক রাজ্যের আছে পারে, এবং এই ধরনের, এবং তারপর পরিবর্তে dictating "ওয়েল, এখানে আমি সকলই এর পারবে না সিক্রেট আলখাল্লা মোডে থাকাকালীন করি, এখানে আমি সকলই এর পারেন সিক্রেট ক্লোকার মোডে করুন "

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.