I am trying to make a four player turn based game.
Now in case their are less than 4 human player I want to start X human player and 4-X AI player. Is it possible with the google turn based multiplayer https://developers.google.com/games/services/android/turnbasedMultiplayer
Also in case a human player leaves I want to replace it with the AI player instead of cancelling the game. Does it support this ?
I am implementing this and have it working 90%. When it is a new turn in my game, I check if the next player is a BOT or HUMAN player. This is prearranged using a seating algorithm, so that I space out the HUMANs neatly. i.e. opposite when there are two human players. When the next player is a BOT, I take a turn, but do not advance the Particpant. That way it will persist and send an update without passing the turn to the other player. When the next player is a HUMAN, I do a take Turn and switch to the next player. The code looks something like this....
if (Players[playerTurn%4]==BOT) {
Games.TurnBasedMultiplayer.takeTurn(MainActivity.mHelper.getApiClient(), MainActivity.mMatch.getMatchId(),
MainActivity.mTurnData.persist(), myParticipantId).setResultCallback(
new ResultCallback() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
state = GameState.Playing;
} else {
Games.TurnBasedMultiplayer.takeTurn(MainActivity.mHelper.getApiClient(), MainActivity.mMatch.getMatchId(),
MainActivity.mTurnData.persist(), nextParticipantId).setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
state = GameState.multiplayerWait;
}
I am not sure how to swap inactive players with another player. I don't think this is possible unless you run your own server or use something like Parse.
Related
Movesense have small memory. With continuous recording from two axes, the memory is full after 13 minutes. There is an idea to stop logging when Movesense does not move (extremely small move) and to recover data after receiving by Timestamp.
Implemented logging of two axes and start-stop recording by a command from Android. Need a help to implement a start-stop recording on a specific condition inside the Movesense, when Android is disconnected from the sensor.
DataLoggerConfig.DataEntry[] entries = {
new DataLoggerConfig.DataEntry("/Meas/Acc/13"),
new DataLoggerConfig.DataEntry("/Meas/Gyro/13")
};
DataLoggerConfig config = new DataLoggerConfig(new DataLoggerConfig.Config(new DataLoggerConfig.DataEntries(entries)));
String jsonConfig = new Gson().toJson(config,DataLoggerConfig.class);
getMDS().put(configUri, jsonConfig, new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException e) {
}
});
On Movesense sensor:
OPTIONAL_CORE_MODULE(DataLogger, true)
OPTIONAL_CORE_MODULE(Logbook, true)
The DataLogger can be started and stopped multiple times by PUT /Mem/DataLogger/State API. For detecting movement you can subscribe to /System/States/0 (=MOVEMENT) api and start & stop recording based on the state change events (the recording will continue to the same log). This can be implemented on mobile as well as sensor code.
Full Disclosure: I work for the Movesense team
I'm trying to modify my application to pause audio playback when Google maps is announcing a turn by turn direction.
I've added the following code (shown below) to my application. The audio focus listener is called when applications like Pandora Radio or Spotify request audio focus in order to play music but it's not called when Google maps announces a turn by turn direction. Is there another intent I should be listening for in order to detect this behavior?
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(new AudioManager.OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
// This is called by Pandora Radio and Spotify
Log.d("Focus change:", " Event is: " + focusChange);
}
}).build());
You will need AudioManager's AudioPlaybackCallback updates.
This only works on Android O and above.
To do this you have to access the audio manager -
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
And then add the listener like this -
Handler handler = new Handler();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
audioManager.registerAudioPlaybackCallback(new AudioManager.AudioPlaybackCallback() {
#Override
public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
super.onPlaybackConfigChanged(configs);
// This will be called when navigation audio state on google maps changes
Log.d("audio active", String.valueOf(audioManager.isMusicActive()));
}
}, handler);
}
The List<AudioPlaybackConfiguration> configs returned in the callback has a AudioAttribute object which contains a string describing the audio playing. For Google maps navigation the String constant value is USAGE_ASSISTANCE_NAVIGATION_GUIDANCE which you can compare to be sure that it is Google Maps announcing the navigation direction.
Programatically you can get it like this
// Loop through the configs to see the media's usage data
configs.get(0).getAudioAttributes().getUsage();
For Android O, the missing navigation duck focus change notifications were only received after I explicitly set my player's AudioAttributes and content type to speech (as I am playing podcast mp3 files, didn't test with other content types):
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
AudioAttributes.Builder aab = new AudioAttributes.Builder();
aab.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH);
aab.setUsage(USAGE_MEDIA);
player.setAudioAttributes(aab.build());
#Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
if (mPlayOnAudioFocus && !isPlaying()) {
play();
} else if (isPlaying()) {
setVolume(MEDIA_VOLUME_DEFAULT);
}
mPlayOnAudioFocus = false;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
setVolume(MEDIA_VOLUME_DUCK);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (isPlaying()) {
mPlayOnAudioFocus = true;
pause();
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
mAudioManager.abandonAudioFocus(this);
mPlayOnAudioFocus = false;
stop();
break;
}
}
}
The following code snippet contains an implementation of this interface for an app that plays audio. And it handles ducking for transient audio focus loss. It also handles audio focus change due to the user pausing playback, vs another app (like the Google Assistant) causing transient audio focus loss
does your app temporarily need audio focus (with the option to duck), since it needs to play an audio notification, or a turn by turn spoken direction, or it needs to record audio from the user for a short period of time? This is
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.
Ducking vs pausing on transient audio focus loss
You can choose to pause playback or temporarily reduce the volume of your audio playback in the OnAudioFocusChangeListener, depending on what UX your app needs to deliver. Android O supports auto ducking, where the system will reduce the volume of your app automatically without you having to write any extra code. In your OnAudioFocusChangeListener, just ignore the AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK event.
In Android N and earlier, you have to implement ducking yourself (as shown in the code snippet above).
for Detail visit :https://medium.com/androiddevelopers/audio-focus-3-cdc09da9c122
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
added in API level 8
public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
Used to indicate a temporary request of audio focus, anticipated to last a short amount of time, and where it is acceptable for other audio applications to keep playing after having lowered their output level (also referred to as "ducking"). Examples of temporary changes are the playback of driving directions where playback of music in the background is acceptable.
You should use "AudioManager.AUDIOFOCUS_GAIN_TRANSIENT", according to the documentation:
AUDIOFOCUS_GAIN_TRANSIENT
Used to indicate a temporary gain or request of audio focus, anticipated to last a short amount of time. Examples of temporary changes are the playback of driving directions, or an event notification.
I am trying to include TBMP into my Mahjong game and this issue has been stuck with me for a few months now, and I am no closer to resolving it.
I have four players in a game and the game progressing counterclockwise. So imagine you have four players at a table:
Player 3
---------------------------
Player 4 | | Player 2
---------------------------
Player 1
Now Player 1 takes a turn, the data is pushed via JSON into the game data, and Player 2 gets notified that it is his turn. Player 3 and 4 also get a notification that a match update has occurred, but when they query the game data, it returns the stale game information, unless it becomes their turn, then they get the accurate and current game data.
I would like all players to be updated after every turn, rather than getting all the updates in one go when it becomes their turn.
I register an update listener as follows:
mTurnBasedMultiplayerClient.registerTurnBasedMatchUpdateCallback(mMatchUpdateCallback);
I have a function that processes the update as follows:
private TurnBasedMatchUpdateCallback mMatchUpdateCallback = new TurnBasedMatchUpdateCallback() {
#Override
public void onTurnBasedMatchReceived(#NonNull TurnBasedMatch turnBasedMatch) {
int turnStatus = turnBasedMatch.getTurnStatus();
// OK, it's active. Check on turn status.
switch (turnStatus) {
case TurnBasedMatch.MATCH_TURN_STATUS_MY_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.Playing;
return;
case TurnBasedMatch.MATCH_TURN_STATUS_THEIR_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.MultiWait;
return;
}
}
#Override
public void onTurnBasedMatchRemoved(#NonNull String matchId) {
game.showToast("A match was removed.");
state = GameState.MultiWait;
return;
}
};
Anyway, the listener is registered correctly and I can see that my listener gets called, but the call to turnBasedMatch.getData() only returns the correct game data when it is also the player's turn.
So Player 4 gets the updates from Player 1, Player 2 and Player 3 only when it becomes his turn. He gets notified that there is an update to the match, but has no way of knowing what the current game data looks like.
Is this the way it is meant to work? Am I doing something wrong?
One workaround for this is to store all the turns inside the message data. This way, I can treat the game data like a Message Queue and replay everything up to the current turn. This is not ideal, but it works.
I am working on a multiplayer domino game where a device acts as a host and makes the decisions for the rest of the players. So far, it works great when everyone has a good connection, but once you introduce some latency, all things go to heck. One problem I'm having is that I need to move some sprites around on command. When everyone has played, the host device will send a message out that tells the remote players who won the hand. After that message is sent, the winner can make the next move.
I move the dominoes to the right using:
void HelloWorld::onChatReceived(AppWarp::chat chatevent)
{
if (chatprefix.compare("_determinewinner_")==0)
{
//chatstring in this case is the playerID who won the hand
MultiPlayerdetermineTrickWinner(atoi(chatstring.c_str()));
}
}
void HelloWorld::MultiPlayerdetermineTrickWinner(int winningplayer)
{
...
for (int i = 0; i <marray_table->count(); ++i)
{
Domino *marray_table_tile = (Domino *)(marray_table->objectAtIndex(i));
CCMoveTo *translate = CCMoveTo::create(0.1f,ccp(pos_x,pos_y));
CCRotateBy *rotleft=CCRotateBy::create(0.1f, 90*int_rotate);
marray_table_tile->runAction(CCSequence::create(translate,rotleft,NULL));
}
...
}
When the players selects a tile, I call
CCMoveTo *translate = CCMoveTo::create(0.1f,ccp(pos_x,pos_y));
selectedtile->runAction(CCSequence::create(translate,NULL));
marray_table->addObject(selectedtile);
The problem I have is, that there are times when the latency is so bad, that the runaction never completes and the tiles don't actually make it to their final position. For instance, if the host player won the hand and makes their move before the other players received the "determinewinner" message. So short of having all the players tell the host when it can move on, how do I schedule a function to occur only after all actions have been completed?
I am writing an android game for teaching kids to count. The instructions are read to the player through sound clips that are put together to form sentences (for instance "Place", "one", "cow", "in the", "barn". This requires a certain amount of reliability when it comes to latency so that the flow of the instructions sounds natural.
Currently I am using MediaPlayer, playing each sound in a OnCompletionListener. Each sound has it's own MediaPlayer that is created and prepared before playback of any sound starts (to reduce latency) - but still I get a significant delay before each sound the first time it is played (the second time it seems some sort of caching has taken place and it works fine).
The sounds are not many and very short and it should probably work better with SoundPool, but SoundPool has no way of knowing when an audio is complete and thus not an option.
Does anyone have any experience with similar problems and a viable solution?
I have used handler with OnCompletionListener and it worked fine for me to give delay between two sounds.
this way,
CommonMethod.player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// /will use count and loop as per number of
// repetition chosen.
handler.postDelayed(new Runnable() {
public void run() {
if (counter >= limit) {
CommonMethod.player.stop();
} else {
CommonMethod.player.start();
}
counter++;
}
}, 3000);
}
});