This forum is read only and just serves as an archive. If you
have any questions, please post them on github.com/phoboslab/impact
Yesterday I made a small plugin to handle buttons and cursors (without using entities).
I just uploaded the first version, I will actively update. I hope you find it useful!
Download, info & docs
https://github.com/datamosh/ImpactJS-GUI
Since yesterday I made multiple changes, now is not only designed for buttons, you can handle groups of elements on the screen, each may have different behaviors.
Time to make the user interface fun! ;)
Heiko plugin is very useful because it has a lot of callbacks for different states and behaviors.
My goal was to make something really easy and mainly without using entities.
So far I saved myself a lot of time with all elements of the interface and menus :P
Can you please provide your button artwork and if possible, a working example. I sort of threw something together based on your pixel amounts as defined in the code, but I'm not sure if it's really working 100%
A lot of these plugins are great, but without working demos, it's hard to see if it's worth it or what I'm looking for.
1 decade ago
by Heiko
@Datamosh
I coded the plugins to not rely on an entity, yet haven't tested without. If you give me a test case I can try and implement without using an entity.
I stand corrected but:
ScaledAlphaHitmask
just requires an Image reference to create hitmask. And the hittest
function would then require x,y coordinates relative to the image, as input.
ButtonStateMachine
just requires isMouseInside
to return true, or false if mouse inside or outside of object/image, and isMouseDown
to return true if mouse is clicked, else false. Then updateState()
needs to be called to update state machine - and this will trigger the callbacks if state changes.
Binding to one or more of the callbacks is optional.
If your image is partially transparent you can use ScaledAlphaHitmask
to determine result passed to isMouseInside
based on the hittest
result returned.
@stahlmanDesign
I added a demo (very small, only a few controls and a pause button to jump & run)
@Heiko
I do not make any test case with images vs. entities. It sounds very interesting!
In my previous games I have buttons as entities and I hate when I search nearby entities filter these objects, maybe I'm just little obsessive :P
This is great. How hard would it be to detect two buttons pressed at one, to run and jump?
Great plugin, I'm really loving it however I've come across a problem that I can't seem to figure out an answer to...
Basically, I've got a group of elements that are set to show/hide on key press, OR on a GUI button's click method. I'm doing this with a simple boolean conditional and the key and button have the same function which is simply to hide/show the GUI group based on the boolean, and then set the boolean to reflect what was just done.
This works great if I only use the key or button, however when I attempt to show it with the key, then hide with the button (or the other way around) it takes multiple clicks/presses, with no change, for the group to then show/hide itself.
Both key and button are using the same boolean variable to judge it's action on, however it is acting as though they are unable to recognize when the other has changed the variable. Because of this, after showing it with the button, you need to press the key 2 times for it to hide the group... or in other words, the key doesn't see that the button has changed the variable, so it does nothing on the first click (which then changes the variable to match what you see) THEN it correctly hides/shows the group.
Here is the code for the key press within the update method:
if (ig.input.released('my_key')) {
if (this.isShown) {
ig.gui.element.action('hideGroup', 'my_group');
this.isShown = false;
} else {
ig.gui.element.action('showGroup', 'my_group');
this.isShown= true;
}
}
... and within the click method of the button itself:
if (this.isShown) {
ig.gui.element.action('hideGroup', 'my_group');
this.isShown = false;
} else {
ig.gui.element.action('showGroup', 'my_group');
this.isShown= true;
}
The only thing that I can think of is that the variable within the button itself is actually referring to the button's variable, and not the entity's... but I'm not entirely sure how to change it's reference, lol
lol, nevermind - I just decided to add a toggle and toggleGroup function to the plugin - idk why i didn't think of that earlier, hahaha
Not sure if anyone would be interested, or if anyone's done it already, but I actually just decided to take the entire concept of binding keys to buttons out of the game and add it to the GUI plugin itself.
I can post the code for it if anyone would like it. It basically just adds a new setting to each GUI element that you add called keybind. By setting it, when you add the new button or element, to a key that is already bound through main.js, it reads that and executes whatever action you have that button set for, including changing the button's state to active, and allowing toggle, and the 'mouseDown' feature, but with the keys.
It also includes the new toggle and toggleGroup functions that can be called to toggle a state. This is useful for 'windows' or popup alerts and the like.
Of course! Send me the code (or make a commit on github)
I will definitely add it to github then - just wanna finish testing it in multiple situations real fast - i might be adding a couple other features to it very soon; I was thinking of giving the ability to have a button-image, and button-icon that renders over the image, all with various states and such.
I also just added a text overlay that is true/false to show the key that is bound to the button - unfortunately it shows the name that you bind the key as in main.js (i've subtr-ed it to use only the first 4 letters of the name. But if you 'name' the binds as what the actual key is, then it gets the job done.
Datamosh: this really is a great gui system, it's very well coded. I'm actually enjoying adding new features to it too, xD
How can I add the draw function to this? I can get lines to draw when I press mouseDown, and I see how that works, but I would like to add a draw function so that some lines are always drawn on the canvas between gui elements as I drag them, and when I let them go and am not clicking.
I tried to add draw function to inject just as the update function is, but it doesn't work.
@Heartless49:
Thank you very much! I want to see the new features! ^_^
@stahlmanDesign:
Ooh! I really do not understand. An example would be great to see the problem!
Inside ig.Game.inject i added Draw and i dont see any problem drawing the screen.
@datamosh:
ill have the code ready later today for github. Just finished adding the icon feature as well as more text features, such as the bind key label, a custom title, and the ability to assign a number value to the buttons which only shows if its > 2. This would be useful for items or something that can deminish. Ill test for bugs in the morning and post it - I wanna try to add tooltips next, xD
@stahlmandesign:
if I understand you correctly, it sounds like you want to be able to have a button that starts another function which causes lines to be drawn from the button to the cursor... or am I mistaken?
Here is an example of what how I am using the gui element for on-ice events in a hockey game (shots, hits, goals, penalties etc.)
Currently when I click on a puck, if it is either a SHOT or GOAL, I draw a line from the gui element's pos.x, pos.y to the corresponding goal on the ice, and the text description appears describing the players involved.
I want the lines to appear all the time, not just on mouseDown. (however the text will appear when mouseDown).
I am not able to figure out where the draw code is for each frame update so that each gui element draws its line to the goal on each update, so it would look like this:
This is what I want the lines to look like
Here is my current code for adding gui elements:
ig.gui.element.add({
playData: playByPlayData.data.game.plays.play[i],
name: 'puck' + i,
group: "",
size: {
x: 16,
y: 16
},
pos: {
x: (puck.x-(16/rinkScaleFactor)) + rx,
y: (puck.y-(16/rinkScaleFactor)) + ry
},
state: {
normal: {
image: new ig.Image('media/rinkIcons.png'),
tile: it,
tileSize: 16
},
active: {
image: new ig.Image('media/rinkIcons.png'),
tile: it,
tileSize: 16
}
},
iconType: iconType,
mouseDown: function() {
console.log(this.playData.desc)
console.log(this.pos.x + ", " + this.pos.y);
//console.log(this.name);
//console.log(this.iconType);
//console.log(this.state.normal.tile);
//console.log(this);
var goalSide = 0; // 605 or 45
if (this.iconType[0] == "r") goalSide = 605;
if (this.iconType[0] == "b") goalSide = 45;
//change alignment of text depending on which side of centre ice
(this.pos.x < 650 / 2) ? this.font.draw(translate(this.playData.desc), this.pos.x + this.size.x, this.pos.y , ig.Font.ALIGN.LEFT) :
this.font.draw(translate(this.playData.desc), this.pos.x, this.pos.y , ig.Font.ALIGN.RIGHT);
if (this.iconType == "redShot" || this.iconType == "blueShot" || this.iconType == "redGoal" || this.iconType == "blueGoal") {
var startX = ig.system.getDrawPos((this.pos.x + this.size.x / 2) - ig.game.screen.x);
var startY = ig.system.getDrawPos((this.pos.y + this.size.y / 2) - ig.game.screen.y);
var endX = ig.system.getDrawPos((goalSide) - ig.game.screen.x); // at one of the two goals which is about 45 pixels from each edge
var endY = ig.system.getDrawPos(277 / 2 - ig.game.screen.y);
ig.system.context.strokeStyle = this.iconType[0] == "r" ? "red" : "blue";
ig.system.context.beginPath();
ig.system.context.moveTo(startX, startY);
ig.system.context.lineTo(endX, endY);
ig.system.context.stroke();
ig.system.context.closePath();
}
},
font: new ig.Font('media/gotham-black.png')
});
Add a new method on Gui, example:
(after //image and before //restore. Use any name, in this case, draw looks good)
// Draw
if(typeof ig.gui.elements[i].draw == 'function')
ig.gui.elements[i].draw.call(element)
And add your element, like this:
ig.gui.element.add({
name: 'pause',
group: 'control',
size: { x: 47, y: 47 },
pos: { x: ig.system.width - 47, y: 5 },
toggle: true,
state: {
normal: {
image: new ig.Image('media/joystick.png'),
tile: 4,
tileSize: 47
},
active: {
image: new ig.Image('media/joystick.png'),
tile: 5,
tileSize: 47
}
},
click: function() {
ig.game.pause = !ig.game.pause
for (var i = 0; i < ig.gui.element.action('getByGroup', 'joystick').length; i++) {
var alpha = 1;
if(ig.game.pause) alpha = 0.5;
ig.gui.element.action('getByGroup', 'joystick')[i].alpha = alpha
ig.gui.element.action('getByGroup', 'joystick')[i].disabled = ig.game.pause
}
},
draw: function() {
var startX = ig.system.getDrawPos((this.pos.x + this.size.x / 2) - ig.game.screen.x);
var startY = ig.system.getDrawPos((this.pos.y + this.size.y / 2) - ig.game.screen.y);
var endX = ig.system.getDrawPos((100) - ig.game.screen.x); // at one of the two goals which is about 45 pixels from each edge
var endY = ig.system.getDrawPos(277 / 2 - ig.game.screen.y);
ig.system.context.strokeStyle = 'red';
ig.system.context.beginPath();
ig.system.context.moveTo(startX, startY);
ig.system.context.lineTo(endX, endY);
ig.system.context.stroke();
ig.system.context.closePath();
}
})
you can add code to this function when you want...
edit
I stand corrected, datamosh to the rescue, xP
Thanks guys, it works. I
updated my link to show the solution.
I've submitted the new features and code to datamosh's git, and he's accepted the changes. I'd just like to explain a couple of the features a bit further, in case the readme leaves and questions unanswered.
As far as the new toggle and toggleGroup actions are concerned, they literally just toggle the visibility of an element, or group of elements. This makes it easier to have windows/menus show/hide themselves with a single command, versus setting up your own toggle code.
The new properties for each element are 100% optional. The keybind property needs to be set to a key that you bound in your Main.js...
ig.input.bind(ig.KEY.B, 'b');
If this is the key you wish to bind an element to, you simply use the 'b' as the keybind...
keybind: 'b',
Next, is the showBind property. This is either true or false and simply states whether or not you want to have the key's name, in this case 'b', shown on the element itself. If you name the key something else, it will be shown on the element instead of the actual key itself. There were probably other ways to go about this, so that you could name the key whatever you wanted and then tell it what to show as the button, but I prefer anything that involves less setup.
Finally the title and showTitle properties. These are just to show a 'name' of some kind on the element. It works in the same manner as keybind and showBind, except that title can be set to whatever you want and showTitle is just the true/false statement to actually show it or not.
I've got a couple other features that I've been working on, but I'm going to finish them up a bit more before I commit them to git.
@datamosh: Hey, i've been messing around with the gui system again, and I've been noticing a slight issue with rendering a decent amount of gui elements.
I'm not exactly sure what it could be, but whenever I click on a button, it causes all of the other elements that are after it, in the elements array, to flash for a brief second.
I've experimented with my code, and really can't see a problem in what I'm doing, as I've tried changing it around and doing things completely different multiple times. Do you think there would be a problem rendering around 50 or so elements at once?
Some not shown, but close to 30-40 are and it causes this 'flash', >.<
I've examined the plugin's code a little bit more, in terms of drawing everything, and the only thing that I can figure is that the for-loop that cycles through everything is causing all of the elements that are AFTER the one that I click, to flash... it doesn't happen to the ones before it, but it really seems like it removed/hides the elements for a split second, then replaces them correctly.
The elements are overlapping? Can you share some code?
No, there not overlapping , they're just flashing whenever you click on a button.
I'll get some code for you in a bit, I'm gonna try changing a few things around some more - all that my code is doing is drawing the elements on startup, but then even when an element as no click event or anything, it still causes everything to flash for some reason.
Ah, found the problem - it was in MY gui.js, a feature I was experimenting with and thought I removed was still in there. Fresh install and it works fine, ^^
Dear all:
I have one small question on this. always made confused.
in your example:joystick.png this picture contains all the buttons.
how you can separate them to display.
I found in your code is written like below mentioned:
for example the 'right' button:
how you can get the left button from the whole image.
the exact description of the 'right' button is x:47 y:47 in the image of(joystick.png)
but the
:normal: {
image: new ig.Image('media/joystick.png'),
tile: 1,
tileSize: 47
}
how system can get the exact 'right' button picture from the whole picture??
thank you for you reply.
g.gui.element.add({
name: 'left',
group: 'joystick',
size: { x: 47, y: 47 },
pos: { x: 0, y: ig.system.height - 47 },
state: {
normal: {
image: new ig.Image('media/joystick.png'),
tile: 0,
tileSize: 47
}
},
mouseDown: function() {
ig.game.player.vel.x = -100
ig.game.player.flip = true
}
})
ig.gui.element.add({
name: 'right',
group: 'joystick',
size: { x: 47, y: 47 },
pos: { x: 47, y: ig.system.height - 47 },
state: {
normal: {
image: new ig.Image('media/joystick.png'),
tile: 1,
tileSize: 47
}
},
Its able to separate it by the tile setting. The tilesize dictates how big each tile is and the tile setting tells is which tile of that image is the correct tile to use.
Its the same concept as entity's animation tiles.
This plug-in is helpful, but the keybind looks like it's not working.
I have
ig.input.bind( ig.KEY.UP_ARROW, 'up');
and
ig.gui.element.add({
keybind: 'up',
...
});
Nothing happens when I press the key.
The mouse click is fine though.
------
EDIT - Fixed That For You:
// Pressed by Mouse OR Keybind
if((state == 'hover' && (ig.input.state('mouse1') || ig.input.pressed('mouse1'))) || (ig.input.pressed(element.keybind))) {
state = 'active';
if(ig.input.state('mouse1') && typeof ig.gui.elements[i].mouseDown == 'function')
ig.gui.elements[i].mouseDown.call(element);
if(ig.input.pressed('mouse1') || ig.input.pressed(element.keybind)) {
// Toggle (click)
if(element.toggle)
element.active = !element.active;
// Click function
if(typeof ig.gui.elements[i].click == 'function')
ig.gui.elements[i].click.call(element);
}
}
I'll push it on GitHub
1 decade ago
by gort
Hey! I'm pretty new to impactjs, so tell me if this is a really dumb queston about your plugin, but: It's not working. I followed the instuctions on you github repository, and looked at the code for your demo, but it still deosn't work.
-Thanks agian (-: