1 decade ago
by RomainT
Hello,
In the game I work on, I want to display a main menu with some items (Start, Help and Credits).
I want to display these menu items with just text and a bullet then I can select an item by hitting my up and down arrow. The bullet must appear in front of the selected item", then if I hit the space, a method is called on the item.
Any idea ? How can I implement this kind of menu ?
Thanks ! :)
It’s not the most automatic of systems but you can write a function that basically does…
“If ‘up’ is pressed and the current selected item is item1 - return false”
“if ‘down’ is pressed and the current selected item is item1 - select item2”
“If ‘up’ is pressed and the current selected item is item2 - select item1”
“if ‘down’ is pressed and the current selected item is item2 - select item3”
“If ‘up’ is pressed and the current selected item is item3 - select item2”
“if ‘down’ is pressed and the current selected item is item3 - return false”
Or you can do away with all that mess and simple map each item to a specific key.
1 decade ago
by RomainT
Here is my very first working-but-naive implementation (with CoffeeScript) and It lives in the draw method of my game class.
draw: ->
@parent()
me = @
@choices = [ 'Start', 'Options', 'Help', 'Credits' ]
@choice ||= null
@selectedItem ||= 0
baseX = ig.system.width / 2
baseY = 50
text = "Startups from Hell"
@font.draw(text, baseX, 12, ig.Font.ALIGN.CENTER)
text = "Make a choice with UP and DOWN then hit SPACE to confirm."
@font.draw(text, baseX, 160, ig.Font.ALIGN.CENTER)
for choice, i in @choices
y = baseY + i * 20
@font.draw(choice, baseX, y, ig.Font.ALIGN.CENTER)
realSelectedItem = Math.abs(@selectedItem) % @choices.length
if realSelectedItem == i
textWidth = @font.widthForString(choice)
x = baseX - textWidth / 2 - @cursorWidth - 8
@font.draw(">>", x, y - 1)
x = baseX + textWidth / 2 + 8
@font.draw("<<", x, y - 1)
if ig.input.pressed('up')
@selectedItem -= 1
else if ig.input.pressed('down')
@selectedItem += 1
else if ig.input.pressed('interact')
realSelectedItem = Math.abs(@selectedItem) % @choices.length
if @choices[realSelectedItem] == 'Start'
me.startGame()
I'm working on cleaner version with two classes : Menu and MenuElement.
1 decade ago
by RomainT
Done.
I now have a module
game.menu containing two classes : Menu and MenuElement.
ig
.module('game.menu')
.defines ->
window.Menu = ig.Class.extend
init: (@font, @choices)->
@selectedChoice = 0
@cursorLeft = ">>"
@cursorLeftWidth = @font.widthForString(@cursorLeft)
@cursorRight = "<<"
@cursorRightWidth = @font.widthForString(@cursorRight)
# Calculate the label length only once.
for choice in @choices
choice.labelWidth = @font.widthForString(choice.label)
# Draw the menu begining at baseY in height.
# The menu is centered around the baseX axis.
draw: (baseX, baseY)->
for choice, i in @choices
y = baseY + i * 20
@font.draw(choice.label, baseX, y, ig.Font.ALIGN.CENTER)
if @selectedChoice == i
x = baseX - choice.labelWidth / 2 - @cursorLeftWidth - 8
@font.draw(@cursorLeft, x, y - 1)
x = baseX + choice.labelWidth / 2 + 8
@font.draw(@cursorRight, x, y - 1)
if ig.input.pressed('up')
@selectChoice(-1)
else if ig.input.pressed('down')
@selectChoice(+1)
else if ig.input.pressed('interact')
@choices[@selectedChoice].interact()
# Change the menu choice in the given shift:
# +1 for bottom, -1 for the top.
selectChoice: (shift)->
index = @selectedChoice + shift
if index < 0
@selectedChoice = @choices.length - 1
else
@selectedChoice = index % @choices.length
# A menu element that have a text label and a callback
# triggered when the item hit.
window.MenuItem = ig.Class.extend
init: (@label, @action)->
interact: ->
@action()
My goal was to have as little computation as possible in the methods called in the game loop.
To use it, I initialize the menu in the
init method of the game :
@menu = new Menu @font, [
new MenuItem "Start", @startGame
new MenuItem "Options", @showOptions
new MenuItem "Help", @showHelp
new MenuItem "Credits", @showCredits
]
Then I draw it in the
draw method of the game :
@menu.draw(baseX, 50)
And it works !
wow it does look pretty in CoffeeScript! never thought of it that way.
1 decade ago
by sleenee
Interesting , but what if you would want to write it in javascript? (I'm not familiar with coffeescript)
Did anyone here ever write a simple menu in javascript?
greetings,
Sleenee
what kind of menu? clickable or based on keyboard?
1 decade ago
by sleenee
I would say clickable but I didn't know it makes that much of a difference. I guess there is nothing in the impact library in order to make building a choice menu / buy and sell menu / player dialog ... anything in that context a bit easier. (not that I saw at least)
So any hints on this?
thanks in advance,
Sleenee
Very nice. A pure javascript version would be sweet.
1 decade ago
by Jack9
I did that this weekend. I translated the menu/item class code to javascript.
ig.module(
'game.menu'
)
.defines(function(){
window.Menu = ig.Class.extend({
init: function(_font,_choices){
this.selectedChoice = 0;
this.cursorLeft = ">>";
this.cursorRight = "<<";
this.cursorLeftWidth = _font.widthForString(this.cursorLeft);
this.cursorRightWidth = _font.widthForString(this.cursorRight);
// Calculate the label length only once.
var i,labeled_choice;
for(i=0;i<_choices.length;i++){
_choices[i].labelWidth = _font.widthForString(_choices[i].label);
}
this.font = _font;
this.choices = _choices;
},
// Draw the menu beginning at baseY in height.
// The menu is centered around the baseX axis.
draw: function(_baseX, _baseY){
var _choices = this.choices;
var _font = this.font;
var i,choice,x,y;
for(i=0;i<_choices.length;i++){
choice = _choices[i];
y = _baseY + i * 12;
_font.draw(choice.label, _baseX, y, ig.Font.ALIGN.CENTER);
if (this.selectedChoice === i){
x = _baseX - (choice.labelWidth / 2) - this.cursorLeftWidth - 8;
_font.draw(this.cursorLeft, x, y - 1);
x = _baseX + (choice.labelWidth / 2) + 8;
_font.draw(this.cursorRight, x, y - 1);
}
}
if(ig.input.pressed('up')){
this.selectedChoice--;
this.selectedChoice = (this.selectedChoice < 0) ? 0 : this.selectedChoice;
}else if(ig.input.pressed('down')){
this.selectedChoice++;
this.selectedChoice = (this.selectedChoice >= _choices.length) ? _choices.length-1 : this.selectedChoice;
}else if(ig.input.pressed('interact')){
_choices[this.selectedChoice].interact();
}
}
// Change the menu choice in the given shift:
// +1 for bottom, -1 for the top.
,selectChoice: function(shift){
var index = this.selectedChoice + shift;
if(index < 0){
this.selectedChoice = this.choices.length - 1;
}else{
this.selectedChoice = index % this.choices.length;
}
}
});
// A menu element that have a text label and a callback
// triggered when the item hit.
window.MenuItem = ig.Class.extend({
init: function(_label, _action){
this.label = _label;
this.action = _action;
},
interact: function(){
this.action();
}
});
});
1 decade ago
by Jack9
I wanted to implement a menu on an EntityCreature, so here's a simplified version of what's in my entity (there may be commas misplaced):
init: function( x, y, settings ) {
var i;
var choices = [
new MenuItem("Option1",this.OptionMethod1),
new MenuItem("Option2",this.OptionMethod2),
new MenuItem("Option3",this.OptionMethod3),
new MenuItem("Option4",this.OptionMethod4)
];
this.menufont = new ig.Font( 'media/04b03.font.png' );
this.contextMenu = new Menu(
this.menufont,
choices
);
// Call the parent constructor
this.parent( x, y, settings );
}
,update: function() {
// This method is called for every frame on each entity.
// React to input, or compute the entity's AI here.
// In your game's or entity's update() method
if( ig.input.pressed('context') ) {
if(this.checkSelection()){
this.context();
}else{
this.decontext();
}
}
// Call the parent update() method to move the entity
// according to its physics
this.parent();
}
,checkSelection:function(){
return (
(ig.input.mouse.x >= this.pos.x && ig.input.mouse.x <= this.pos.x+this.animSheet.width)
&& (ig.input.mouse.y >= this.pos.y && ig.input.mouse.y <= this.pos.y+this.animSheet.height)
);
}
,decontext:function(){
this.contexted = false;
}
,context:function(){
if(!this.contexted){
this.contexted = true;
}
}
,draw: function() {
// Draw all entities and backgroundMaps
if(this.contexted && this.contextMenu){
this.contextMenu.draw(this.pos.x+(this.animSheet.width/2),this.pos.y+(this.animSheet.height));
}
this.parent();
}
Finally, in my main draw I put:
draw: function() {
// Draw all entities and backgroundMaps
this.parent();
if( ig.input.pressed('interact')) {
var i,creatures;
creatures = this.getEntitiesByType("EntityCreature");
// clear all other selections
for(i=0;i<creatures.length;i++){
if(creatures[i].contexted){
creatures[i].decontext();
}
}
}
}
That took a lot to figure out, but it works as I would expect.
1 decade ago
by sleenee
@Jack9
This is a really brilliant solution, thank you for this!
I will try to experiment with your code as basis.
Sleenee
I just tried implementing Jack9's menu, but I can't seem to get it working. I'm not even entirely sure how or where it's supposed to appear. Was wondering if anyone could offer some simple instructions for setting this (or similar) up. Thank you!
Page 1 of 1
« first
« previous
next ›
last »