Jump to content

Announcing our new website

To find out the latest about our new website, visit Game Dev Unlimited

- - - - -

[Help] Button Inputs


  • You cannot reply to this topic
9 replies to this topic

#1 bigace

    King of Spades

  • Member
  • 730 posts

Posted 17 May 2012 - 01:08 PM

I was wondering if anyone knew of a way to close a scene by clicking any button on the keyboard and not just the usually:
    if Input.trigger?(Input::B)
	  $game_system.se_play($data_system.cancel_se)
	  $scene = Scene_Map.new
	  return
    end


#2 kellessdee

    mrrow ~!

  • Moderator
  • 893 posts

Posted 17 May 2012 - 02:48 PM

You could make a hash or something, that maps keys to Scenes:

Keys_To_Scene = {
  Input::B => Scene_Map
}

Keys_To_Scene.each_key do |key|
  if Input.trigger?(key)
    $scene = Keys_To_Scene[key].new
    break
  end
end

Or something similar. Unfortunately, since there isn't an easy way to figure out which key is being pressed you're probably going to have to loop through each key in some way. A hash should be a simple way to Map the values anyways.

#3 bigace

    King of Spades

  • Member
  • 730 posts

Posted 17 May 2012 - 05:32 PM

So something like this:
Keys_To_Scene = {
Input::DOWN  => Scene_Map
Input::LEFT  => Scene_Map
Input::RIGHT => Scene_Map
Input::UP	=> Scene_Map
Input::A => Scene_Map
Input::B => Scene_Map
Input::C => Scene_Map
Input::X => Scene_Map
Input::Y => Scene_Map
Input::Z => Scene_Map
Input::L => Scene_Map
Input::R => Scene_Map
Input::SHIFT => Scene_Map
Input::CTRL  => Scene_Map
Input::ALT   => Scene_Map
Input::F5 => Scene_Map
Input::F6 => Scene_Map
Input::F7 => Scene_Map
Input::F8 => Scene_Map
Input::F9 => Scene_Map
}
Keys_To_Scene.each_key do |key|
if Input.trigger?(key)  
  $scene = Keys_To_Scene[key].new  
  break
end
end

So is there a way to make this shorter, because this is a long list of commands. Something like,
Keys_To_Scene = {}
Keys_To_Scene[Scene_Map] => {Input::DOWN, Input::LEFT, Input::RIGHT, Input::UP,
Input::A, Input::B, Input::C, Input::X, Input::Y, Input::Z, Input::L, Input::R, 
Input::SHIFT, Input::CTRL, Input::ALT, Input::F5, Input::F6, Input::F7,
Input::F8, Input::F9}

Edited by bigace, 17 May 2012 - 05:59 PM.


#4 kellessdee

    mrrow ~!

  • Moderator
  • 893 posts

Posted 17 May 2012 - 05:46 PM

Not really, either way you go about it, you're gonna have to map out the commands. However, if what you're worried about is the loop (to tes which key) i can send you an edit i did of blizzard's custom key input script, where it returns the key being pressed, rather than true/false based on an input

#5 bigace

    King of Spades

  • Member
  • 730 posts

Posted 17 May 2012 - 06:01 PM

oh lol I didn't knew you posted already I edited my previous post, but I doubt that way will as effective anyways. Sure I'll take a look at the blizzard script.

#6 kellessdee

    mrrow ~!

  • Moderator
  • 893 posts

Posted 17 May 2012 - 08:58 PM

Here's blizz's script with modifications. I was using the method so I could allow custom player key bindings.
I left in blizzard's instructions and of course, the license is included.
Spoiler

There are a couple of methods in there (Input.key? and Input.keys?)

Input.keys?
Returns an array of integers (ascii values) of the last keys TRIGGERED

Input.key?
Slightly optimized version of Input.keys?, only returns the integer (ascii value) of the last key TRIGGERED (it would be similar to saying: Input.key?[0] or Input.key?.first)

NOTE: Some keys (such as Shift) will usually return 2 values (Shit and Left Shift or Right Shift) and it may NOT necessarily be the value you intended. Any values that do not exist in the key map WILL not be returned, i.e. removing "Shift" from the map will force only "Left Shift" or "Right Shift" to be returned.

So, to use this to do what you are trying to do:

1st Solution: Blizzard uses Arrays to store multiple key bindings for each key, for this next part to work properly, you'll want to use one key per binding (non-array), so the single key values will map to the hash properly

Keys_To_Scene = {
  # Omitted for brevity
  Input::A => Scene_Map,
  # Omitted for brevity
}

# Call scene from key:
key = Input.key?
scene = Keys_To_Scene[key]
if scene
  $scene = scene.new
end

2nd Solution: If you are like me, you'll like the notion of different keys mapping to a single game key, (like Input::B corresponds to x, and escape, etc.)
To do this, we'll have to modify the keys? and key? methods, to return something a little more meaningful than ascii values (although, arguably, this is the most meaningful information ABOUT a key)

So, my thought might be to add another wrapping list, to then map the ascii values to an actual key constant.
KEY_LIST = [
  UP,
  LEFT,
  DOWN,
  RIGHT,
  A,
  B,
  C,
  X,
  Y,
  Z,
  L,
  R,
  F5,
  F6,
  F7,
  F8,
  F9
  SHIFT,
  CTRL,
  ALT
]

Then, just tweak the keys? and key? methods:

def self.keys?
  input = []
  keys = Key.invert
  @triggered.each_index { |i|
	if @triggered[i] && keys[i]
	  KEY_LIST.each { |k|
		if k.include?(i)
		  input << k
		  break
		end
	  }
	end
  }
  return input
end


def self.key?
  input = []
  keys = Key.invert
  @triggered.each_index { |i|
	if @triggered[i] && keys[i]
	  KEY_LIST.each { |k|
		if k.include?(i)
		  return k
		end
	  }
	end
  }
  return nil
end

Now, you can do the same as the first solution, except it will play nicely with arrays (so, multiple key bindings)
Unfortunately it adds another loop to it all, and puts some ugly dependencies, and doesn't necessarily follow the DRY principle.

Although, I am definitely without a doubt that this entire script could be redesigned to support these concepts, and still be compatible with the standard RMXP input style.

Another improvement, might be to create & cache the Key.invert at the start of the game, that way every call to Input.key? doesn't have to do that for you.

It should also be easy to extend this to support "pressed" and "released" keys, just replace @triggered with @pressed, @released or @repeated.

EDIT:

Sorry, those solutions were bugging me. Here's a cleaner solution, and is plug and play. No configurations/workarounds necessary:
Spoiler

This provides support for:

Input.triggered_keys?
Input.triggered_key?

Input.pressed_keys?
Input.pressed_key?

Input.repeated_keys?
Input.repeated_key?

And, it wraps nicely around the default Input module.

Also, this will work "out of the box"

Keys_To_Scene = {
  # Omitted for brevity
  Input::A => Scene_Map,
  # Omitted for brevity
}

# Call scene from key:
key = Input.triggered_key?
if Keys_To_Scene.has_key?(key)
  $scene = Keys_To_Scene[key].new
end


#7 bigace

    King of Spades

  • Member
  • 730 posts

Posted 18 May 2012 - 10:31 PM

Thanks for the edited version, as I'm about to release a script thats not that big and I don't need blizzards huge script for something thats this small.

Small problem when I try to open the scene I created I get a error on line 18 after added the your new methods to the module Input when I try to access it from the menu or the map.

Quote

Script 'Module Input; line 18: TypeError occured.
cannot convert String tinto Interger.


module Input
include ACE::Help
class << self
  alias ace_init_quick_awards_lat update unless $@
  def triggered_keys?
   input = []
   constants.each do |const|
    key = const_get(const.to_sym)
    if trigger?(key)
	 input << key
    end
   end
   return input
  end
  def triggered_key?
   constants.each do |const|
    key = const_get(const.to_sym)
    if trigger?(key)
	 return key
    end
   end
   return nil
  end
  def pressed_keys?
   input = []
   constants.each do |const|
    key = const_get(const.to_sym)
    if press?(key)
	 input << key
    end
   end
   return input
  end
  def pressed_key?
   constants.each do |const|
    key = const_get(const.to_sym)
    if press?(key)
	 return key
    end
   end
   return nil
  end
  def repeated_keys?
   input = []
   constants.each do |const|
    key = const_get(const.to_sym)
    if repeat?(key)
	 input << key
    end
   end
   return input
  end
  def repeated_key?
   constants.each do |const|
    key = const_get(const.to_sym)
    if repeat?(key)
	 return key
    end
   end
   return nil
  end
end
def self.update_old; ace_init_quick_awards_lat; end
def self.check_blocked
  Block_Scenes.each {|s| return true if $scene.is_a?(s)}
  return false
end
def self.update
  if Quick_Access != nil && Input.trigger?(Quick_Access) &&
   @scene == nil && !self.check_blocked
   $game_system.se_play($data_system.decision_se)
   @scene = Scene_Help.new
   @scene = nil
  end
  ace_init_quick_awards_lat
end
end


#8 kellessdee

    mrrow ~!

  • Moderator
  • 893 posts

Posted 19 May 2012 - 02:44 AM

Well, it's because I took the lazy path, and relied on the fact that the only constants defined for Input would be the key values. By bringing in ACE::Help you are probably pulling in extra constants, which will be evaluated in
constants.each do |const|
  # ... etc ...
end

That's why you are getting the error "string can't be converted into integer", as line 18 (trigger?(key)) expects an integer, not a string (and I'll bet one of the constants pulled in, is a string.)

What you can do is, take the proper route:
module Input
  KEYS = [
    DOWN,
    LEFT,
    RIGHT,
    UP, 
    A,
    B,
    C,
    X,
    Y,
    Z,
    L,
    R, 
    SHIFT,
    CTRL,
    ALT,
    F5,
    F6,
    F7, 
    F8,
    F9
  ]

  class << self

    def triggered_keys?
      input = []
      KEYS.each do |key|
        if trigger?(key)
          input << key
        end
      end
      return input
    end

    def triggered_key?
      KEYS.each do |key|
        if trigger?(key)
          return key
        end
      end
      return nil
    end

    def pressed_keys?
      input = []
      KEYS.each do |key|
        if press?(key)
          input << key
        end
      end
      return input
    end

    def pressed_key?
      KEYS.each do |key|
        if press?(key)
          return key
        end
      end
      return nil
    end

    def repeated_keys?
      input = []
      KEYS.each do |key|
        if repeat?(key)
          input << key
        end
      end
      return input
    end

    def repeated_key?
      KEYS.each do |key|
        if repeat?(key)
          return key
        end
      end
      return nil
    end

  end

end

There, now it shouldn't matter what constants you bring in, as long as you aren't redefining the existing constants (keys)

#9 bigace

    King of Spades

  • Member
  • 730 posts

Posted 19 May 2012 - 05:16 AM

I just posted the script that this module is part of: Scene_Help If you want to take a look.

I might of done something wrong but when I tryed doing that the scene wouldn't close? The one I posted doesn't have the new methods in module Input I'm still working on it.

#10 kellessdee

    mrrow ~!

  • Moderator
  • 893 posts

Posted 21 May 2012 - 03:56 PM

Hmm, I'd have to see your implementation, but, shouldn't the Scene objects handle the closing and what not themselves?

Like, user presses button and then this is called:
$scene = Scene_Whatever.new
then, the loop in the current scene will say "hey! $scene != self! cleanup time" (according to RMXP design)
then, the loop in the new scene will start up.
Finally, when the user exits that scene (or however it's set up to leave) it will do the same.

But, I dunno. Thought experiments always seem work better than in practice...let's see what you are trying to do.





0 user(s) are reading this topic

members, guests, anonymous users