This is a documentation for Board Game Arena: play board games online !
Game interface logic: yourgamename.js: تفاوت میان نسخهها
خط ۲۹۲: | خط ۲۹۲: | ||
alert( "myArgument = " + notif.args.myArgument ); | alert( "myArgument = " + notif.args.myArgument ); | ||
} | } | ||
</pre> | |||
=== Synchronous notifications === | |||
When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. | |||
However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. | |||
As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. | |||
Here's how we do this, right after our subscription: | |||
<pre> | |||
dojo.subscribe( 'playDisc', this, "notif_playDisc" ); | |||
this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler | |||
</pre> | </pre> | ||
نسخهٔ ۳۰ ژانویهٔ ۲۰۱۳، ساعت ۱۵:۲۷
This is the main file for your game interface. Here you will define:
- which actions on the page will generate calls to the server
- what happens when you get a notification for change from the server and how it will show in the browser.
File structure
The details on how the file is structured is described directly with comments on the code skeleton provided to you.
Basically, here's this structure:
- constructor: here you can define variable global to your whole interface.
- setup: this method is called when the page is refreshed, in order you can setup the game interface.
- onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state.
- onLeavingState: the method is called when leaving a game state.
- onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar.
- (utility methods): at this place you can define your utility methods
- (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item).
- setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface.
- (notification handlers): at this place you can define your notifications handlers.
General tips
- this.player_id
- Id of the player on whose browser the code is running.
- this.isSpectator
- Flag set to true if the user at the table is a spectator (not a player).
- this.gamedatas
- Contains your initial set of datas to init the game, created at game start or game refresh (F5)
- You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't).
Dojo framework
BGA is using the Dojo Javascript framework.
The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot.
To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page.
Access and manipulate the DOM
$('some_html_element_id')
The $() function is used to get some HTML element using its "id" attribute.
Example 1: modify the content of a "span" element:
In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999";
Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function.
dojo.style
With dojo.style you can modify a CSS property of any HTML element of your interface.
Examples:
// Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' );
Note: you must always use dojo.style to modify CSS properties of HTML elements.
Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below).
dojo CSS classes manipulation
In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually).
Advantages are:
- All your CSS stuff remains in your CSS file.
- You can add/remove a list of CSS modifications with a simple function and whithout error.
- You can test if you applied the stuff to an element with "dojo.hasClass" method.
Example from "Reversi":
// We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' );
Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :)
dojo.query
With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style.
Example:
// All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length;
But what is really cool with dojo.query is that you can combine it with almost all methods above.
Examples:
// Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' );
dojo.place
dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=" method as soon as you must insert HTML tags and not only values.
// Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" );
Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... See full doc on dojo.place.
Usually, when you want to insert some piece of HTML in your game interface, you should use "Javascript templates".
addStyleToClass: function( cssClassName, cssProperty, propertyValue )
Same as dojo.style(), but for all the nodes set with the specified cssClassName
Animations
- slideToObject
- function( mobile_obj, target_obj, duration, delay )
- Return an dojo.fx animation that is sliding a DOM object from its current position over another one
- Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo.
- slideToObjectPos
- function( mobile_obj, target_obj, target_x, target_y, duration, delay )
- Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object.
Players input
dojo.connect
Used to associate a player event with one of your notification method.
Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"):
dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' );
Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else.
this.checkAction( "my_action_name" )
Usage: checkAction: function( action, nomessage )
Check if player can do the specified action by taking into account:
- current game state
- interface locking (a player can't do any action if an action is already in progress)
return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state).
return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress".
Example:
function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } }
this.ajaxcall( url, parameters, obj_callback, callback, callback_error )
This method must be used to send a player input to the game server.
- url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html"
- parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call.
- obj_callback: must be set to "this".
- callback: a function to trigger when the server returns and everything went fine.
- callback_error: (optional and rarely used) a function to trigger when the server returns an error.
Usage:
this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } );
this.confirmationDialog()
Display a confirmation dialog with a yes/no choice.
We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players.
Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes );
Example:
this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) );
- addEventToClass
- function( cssClassName, eventName, functionName )
- Same as dojo.connect(), but for all the nodes set with the specified cssClassName
Translations
See Translations
Notifications
When something happens on the server side, your game interface Javascript logic received a notification.
Here's how you can handle these notifications on the client side.
Subscribe to notifications
Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code.
Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example):
// In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" );
Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method.
Then, you have to define your "notif_playDisc" method:
notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); },
In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args".
Example:
// If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); }
Synchronous notifications
When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic.
However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so.
As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played.
Here's how we do this, right after our subscription:
dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler
Tooltips
this.addTooltip( node, _( helpString ), _( actionString ), delay )
Add a simple text tooltip to the DOM node.
Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?".
You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one.
Usually, _() must be used for the text to be marked for translation.
"Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines).
Example:
this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' );
this.addTooltipHtml( node, html, delay )
Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card).
this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )
Add a simple text tooltip to all the DOM nodes set with this cssClass.
IMPORTANT: all concerned nodes must have IDs to get tooltips.
addTooltipHtmlToClass( cssClass, html, delay )
Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card).
IMPORTANT: all concerned nodes must have IDs to get tooltips
BGA GUI components
BGA framework provides some useful ready-to-use components for the game interface:
Studio#BGA_Studio_game_components_reference
Other useful stuff
dojo.hitch
With dojo.hitch, you can create a callback function that will run with your game object context whatever happen.
Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch:
this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) );
In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called.
- updateCounters(counters)
- Useful for updating game counters in the player panel (such as resources).
- 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ]
- All counters must be referenced in this.gamedatas.counters and will be updated.
- DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'.
- isCurrentPlayerActive()
- Returns true if the player on whose browser the code is running is currently active (it's his turn to play)
- showMessage
- function( msg, type )
- Show an information message during a few seconds at the top of the page
- Type can be 'error' or 'info'
- this.scoreCtrl[ player_id ].incValue( score_delta );
- Adds score_delta (positive or negative integer) to the current score value for player