Upload
vankien
View
223
Download
0
Embed Size (px)
Citation preview
1
Creating Multi-Player Experiences Using NFC and Wi-Fi* Direct On
Intel® Atom™ Processor-Based Tablets
Abstract
This article intends to serve as a tutorial on how to use near field communications (NFC) and Wi-Fi
Direct in developing multi-device and multi-user application experiences on Intel® Atom™ processor-
based tablets running Microsoft Windows 8*. A simple game app is used as an example to describe
how to enable the proximity capabilities in the new Windows Store apps.
1 NFC and Wi-Fi Direct on Intel Atom Processor-based Tablets
Intel Atom processor-based tablets running Windows 8 support Near Field Communications (NFC)
and Wi-Fi Direct through Windows.Networking.Proximity namespace API.
NFC is a set of communication standards that allows peer-to-peer data exchanges between two
closely held devices. If two devices are held closely to each other (less than 4 centimeters) or are
simply “tapped” together, the NFC components on the devices can discover each other and the
operating systems can set up connection between the two devices to exchange data.
Wi-Fi Direct is a technology that allows Wi-Fi devices to directly connect to each other without
connecting to a wireless access point. Using Wi-Fi Direct, a device can browse for other devices
within the Wi-Fi radio range, and request direct connection with these devices.
Based on the Windows Proximity API on NFC and Wi-Fi Direct, we can create multi-device, multi-user
experiences on tablets with Intel Atom processors.
2 Windows Proximity API
2
On Windows 8, the Windows.Networking.Proximity namespace API provides access to NFC and Wi-Fi
Direct hardware features. In the following sections, we will go through the process of developing a
“Tic-Tac-Toe” game app (Figure 1) using Microsoft Visual Studio 2012* and C# to show how to use
Windows Proximity API to create connections between two proximity devices.
An important note is this app must be run on two devices equipped with a NFC component and/or a
Wi-Fi adapter that supports Wi-Fi Direct.
Figure 1 The Tic-Tac-Toe game
3 Developing a Multi-Player Game Using Windows Proximity API
To start the Tic-Tac-Toe game app development, we assume you have already installed and set up
Visual Studio 2012 on your Windows 8 development system, and you have basic knowledge of the
C# programming language and XAML. To learn using C# and XAML to develop Windows Store apps,
please go to the link http://msdn.microsoft.com/en-us/library/windows/apps/hh974581.aspx.
3
3.1 Create a C# Project and Enable Proximity Capabilities
Let’s start with creating the C# project. Open Visual 2012, select File->New->Project…, then select
“Visual C#” on the left pane, and “Blank App (XAML)” on the right pane. In the project name field,
enter “TicTacToe” (Figure 2), and click “OK”.
Figure 2 Creating a new C# project
Now we have the project created. On the “Solution Explorer” window we can see a file named
“Package.appxmanifest”. Double click the file to open it, and select the “Capabilities” tab within the
view of the file, and check the “Proximity” capability item (Figure 3).
4
Figure 3 Adding proximity in the application capabilities
3.2 Add the UI XAML Code
Now let’s define the Tic-Tac-Toe game UI in XAML. On Solution Explorer screen, double click the file
“MainPage.xaml” to open it. Copy the contents in Figure 4 and paste to overwrite and replace the
original MainPage.xaml file content.
<Page
x:Class="TicTacToe.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TicTacToe"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Height="Auto" VerticalAlignment="Top"
HorizontalAlignment="Left" Grid.Column="0">
<GridView x:Name="TicTacToeGridView" Width="Auto"
5
Height="Auto" Background="Green"
BorderBrush="LightGray"
BorderThickness="2" VerticalAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
IsItemClickEnabled="True"
>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"
MaximumRowsOrColumns="3"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<Button x:Name="Grid_Button00"
Click="HandleClick_Button00" Height="200" Width="200" />
<Button x:Name="Grid_Button01"
Click="HandleClick_Button01" Height="200" Width="200" />
<Button x:Name="Grid_Button02"
Click="HandleClick_Button02" Height="200" Width="200" />
<Button x:Name="Grid_Button03"
Click="HandleClick_Button03" Height="200" Width="200" />
<Button x:Name="Grid_Button04"
Click="HandleClick_Button04" Height="200" Width="200" />
<Button x:Name="Grid_Button05"
Click="HandleClick_Button05" Height="200" Width="200" />
<Button x:Name="Grid_Button06"
Click="HandleClick_Button06" Height="200" Width="200" />
<Button x:Name="Grid_Button07"
Click="HandleClick_Button07" Height="200" Width="200" />
<Button x:Name="Grid_Button08"
Click="HandleClick_Button08" Height="200" Width="200" />
</GridView>
</StackPanel>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left"
Margin="10,10,10,10" Grid.Column="1" >
<Button x:Name="StartPlayGameButton" Content="Start Game"
Background="Blue" Visibility="Collapsed" Margin="0,0,10,0"/>
<Button x:Name="FindPlayersButton" Content="Find Players"
Background ="Blue" Visibility="Collapsed" Margin="0,0,10,0"/>
<Button x:Name="InvitPlayerButton" Content="Invite A
Player" Background="Blue" Visibility="Collapsed" Margin="0,0,10,0"/>
<Button x:Name="AcceptButton" Content="Accept The
Invitation" Background="Blue" Visibility="Collapsed"
Margin="0,0,10,0"/>
<Button x:Name="EndGameButton" Content="End This Game"
Background="Blue" Visibility="Collapsed" Margin="0,0,10,0"/>
<Button x:Name="ReplayGameButton" Content="Play Again"
Background="Blue" Visibility="Collapsed" Margin="0,0,10,0"/>
<TextBlock x:Name="OutputTextBlock" FontSize="20"
Margin="0,0,10,0" />
<ListBox x:Name="ProximityDeviceList"
Background="LightBlue" >
</ListBox>
</StackPanel>
</Grid>
</Page>
6
Figure 4 The MainPage.xaml file (**)
You will see the Tic-Tac-Toe basic UI appear on the design view (Figure 5).
Figure 5 The MainPage.xaml Design view
3.3 Attach PeerFinder Event Handlers and Start PeerFinder
In this game app, we support setting up device connections using either NFC tap gestures or Wi-Fi
Direct peer browsing. This is done by handling the PeerFinder.TriggeredConnectionStateChanged
and PeerFinder.ConnectionRequested events. We attach these event handlers in the “Start Game”
button click event handler. We then start PeerFinder. Figure 6 shows the “Start Game” button click
event handler code, which is in the C# file named MainPage.xaml.cs.
void StartPlayGame(object sender, RoutedEventArgs e)
{
if (!_peerFinderStarted)
{
// tap triggered state change handler
7
PeerFinder.TriggeredConnectionStateChanged += new
TypedEventHandler<object,
TriggeredConnectionStateChangedEventArgs>(TriggeredConnectionStateChang
edEventHandler);
// ConnectionRequested event handler
PeerFinder.ConnectionRequested += new
TypedEventHandler<object,
ConnectionRequestedEventArgs>(ConnectionRequestedEventHandler);
// start the proximate device peer finder
PeerFinder.Start();
_peerFinderStarted = true;
…
}
}
Figure 6 Attaching the PeerFinder event handlers (**)
3.4 Handle PeerFinder Events
Now let’s implement the two PeerFinder event handlers. In the connection triggered by tap gestures,
after the connection is set up, we start data read and write using the Proximity stream socket
created during the connection setting up. If the connection is initiated using Wi-Fi Direct peer
browsing, we will display an “Accept” button to let the user confirm the connection.
async private void
TriggeredConnectionStateChangedEventHandler(object sender,
TriggeredConnectionStateChangedEventArgs eArgs)
{
if (eArgs.State == TriggeredConnectState.PeerFound)
{
// devices tap and detect each other, socket connection
set is setup.
DisplayConsoleMessage("Starting the connection ...");
}
if (eArgs.State == TriggeredConnectState.Completed)
{
DisplayConsoleMessage("Connect has been set up");
// get the socket which has been set up
_connectionSocket = eArgs.Socket;
await
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.StartSocketReadWrite();
});
}
8
if (eArgs.State == TriggeredConnectState.Failed)
{
DisplayConsoleMessage("Failed to set up socket
connection.");
}
}
private async void ConnectionRequestedEventHandler(object
sender, ConnectionRequestedEventArgs args)
{
_invitedPlayerDevice = args.PeerInformation;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()
=>
{
DisplayConsoleMessage("Another player is available on
" + args.PeerInformation.DisplayName);
this.AcceptButton.Visibility = Visibility.Visible;
});
}
Figure 7 The implementation of the PeerFinder event handlers (**)
3.5 Exchange Messages Using the Proximity Stream Socket
After the connection between the two devices is set up, the two devices can exchange messages
using the StreamSocket object created by the Proximity API (Figure 8). This game app uses these
back-and-forth messages to inform the other player about the game moves and synchronize the
current state of the game.
void StartSocketReadWrite()
{
StartPlayGameButton.Visibility = Visibility.Collapsed;
InvitPlayerButton.Visibility = Visibility.Collapsed;
AcceptButton.Visibility = Visibility.Collapsed;
ProximityDeviceList.Visibility = Visibility.Collapsed;
FindPlayersButton.Visibility = Visibility.Collapsed;
_reader = new DataReader(_connectionSocket.InputStream);
_writer = new DataWriter(_connectionSocket.OutputStream);
_connectionSocketClosed = false;
ReadSocketMessage();
}
async void ReadSocketMessage()
{
try
{
uint byteCount = await _reader.LoadAsync(sizeof(uint));
if (byteCount > 0)
{
9
uint len = (uint)_reader.ReadUInt32();
byteCount = await _reader.LoadAsync(len);
if (byteCount > 0)
{
String message = _reader.ReadString(len);
ProcessReadMessage(message);
// get the move of the other player, now is my
turn to play
_turnToPlay = true;
ReadSocketMessage(); // waiting for the next
socket read
}
else
{
HandleSocketError("The socket reading
failed.");
}
}
else
{
HandleSocketError("The socket reading failed.");
}
}
catch (Exception e)
{
if (!_connectionSocketClosed)
{
HandleSocketError("The socket reading failed with
the reason " + e.Message);
}
}
}
async private void SendMessage(String msg)
{
DisplayConsoleMessage("");
String message = msg;
if (!_connectionSocketClosed)
{
if (message.Length > 0)
{
try
{
uint len = _writer.MeasureString(message);
_writer.WriteUInt32(len);
_writer.WriteString(message);
uint byteCount = await _writer.StoreAsync();
if (byteCount > 0)
{
DisplayConsoleMessage("Send the move to the
other player: " + message + " byte count: " + byteCount);
}
else
{
HandleSocketError("The communication with
10
the other player interrupted.");
}
}
catch (Exception err)
{
if (!_connectionSocketClosed)
{
HandleSocketError("Communication with the
other player error: " + err.Message);
}
}
}
}
else
{
HandleSocketError("The communication with the other
player device is closed.");
}
}
Figure 8 Using the Proximity StreamSocket to exchange messages (**)
3.6 Other Game Logic
Besides the logic used to set up the connection between the two devices in proximity,
MainPage.xaml.cs includes code that implements the Tic-Tac-Toe game logic, for example, to
determine which player’s turn it is to play, if the game ends, etc. We attach the completed
MainPage.xaml.cs file here. On the Solution Explorer screen, you may double click the
MainPage.xaml.cs file to open it, and copy the contents of Figure 9 and paste to overwrite the
contents in your original MainPage.xaml.cs file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
11
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using System.Diagnostics;
// The Blank Page item template is documented at
http://go.microsoft.com/fwlink/?LinkId=234238
namespace TicTacToe
{
/// <summary>
/// An empty page that can be used on its own or navigated to
within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private IReadOnlyList<PeerInformation> _deviceList;
private PeerInformation _invitedPlayerDevice;
private StreamSocket _connectionSocket = null;
private bool _connectionSocketClosed = true;
private bool _peerFinderStarted = false;
private DataWriter _writer;
private DataReader _reader;
private bool _triggeredSupported = false;
private bool _browseSupported = false;
bool _launchedActivatedByPeerFinder = false;
//stores the current state of a game cell, 0 - untaken 1-taken
by player 1, 2-taken by plaer 2
private int[] _grid;
// 1 - player 1, 2- payer 2
private int _playerID;
//my turn to play
private bool _turnToPlay = false;
//the game has reached an end game state, one side wins or the
game draws
private bool _gameEnds = false;
private Button[] _buttons;
// the messages used to exchange the current state of the game
private static string _iwin = "IWIN";
private static string _gameDraw = "DRAW";
private static string _replayRequest = "REPLAY";
12
private static string _endGameRequest = "ENDGAME";
public MainPage()
{
this.InitializeComponent();
_buttons = new Button[9];
_buttons[0] = Grid_Button00;
_buttons[1] = Grid_Button01;
_buttons[2] = Grid_Button02;
_buttons[3] = Grid_Button03;
_buttons[4] = Grid_Button04;
_buttons[5] = Grid_Button05;
_buttons[6] = Grid_Button06;
_buttons[7] = Grid_Button07;
_buttons[8] = Grid_Button08;
_grid = new int[9];
for (int ix = 0; ix < 9; ix++)
{
_grid[ix] = 0;
}
_playerID = 0;
_triggeredSupported = (PeerFinder.SupportedDiscoveryTypes &
PeerDiscoveryTypes.Triggered) ==
PeerDiscoveryTypes.Triggered;
_browseSupported = (PeerFinder.SupportedDiscoveryTypes &
PeerDiscoveryTypes.Browse) ==
PeerDiscoveryTypes.Browse;
if (_triggeredSupported || _browseSupported)
{
StartPlayGameButton.Click += new
RoutedEventHandler(StartPlayGame);
FindPlayersButton.Click += new
RoutedEventHandler(FindPlayers);
InvitPlayerButton.Click += new
RoutedEventHandler(InvitePlayer);
AcceptButton.Click += new RoutedEventHandler(Accept);
ReplayGameButton.Click += new
RoutedEventHandler(Replay);
EndGameButton.Click += new RoutedEventHandler(EndGame);
StartPlayGameButton.Visibility = Visibility.Visible;
DisplayConsoleMessage("Press the Start Game to start a
new game.");
}
}
void StartPlayGame(object sender, RoutedEventArgs e)
{
DisplayConsoleMessage("");
if (!_peerFinderStarted)
{
// tap triggered state change handler
PeerFinder.TriggeredConnectionStateChanged += new
13
TypedEventHandler<object,
TriggeredConnectionStateChangedEventArgs>(TriggeredConnectionStateChang
edEventHandler);
// ConnectionRequested event handler
PeerFinder.ConnectionRequested += new
TypedEventHandler<object,
ConnectionRequestedEventArgs>(ConnectionRequestedEventHandler);
// start the proximate device peer finder
PeerFinder.Start();
_peerFinderStarted = true;
if (_triggeredSupported && _browseSupported)
{
DisplayConsoleMessage("Tap two player devices
together, or click Find Players button.");
FindPlayersButton.Visibility = Visibility.Visible;
}
else if (_triggeredSupported)
{
DisplayConsoleMessage("Tap two player devices
together to set up connection.");
}
else if (_browseSupported)
{
DisplayConsoleMessage("Click Find Players
button.");
}
}
}
async private void
TriggeredConnectionStateChangedEventHandler(object sender,
TriggeredConnectionStateChangedEventArgs eArgs)
{
if (eArgs.State == TriggeredConnectState.PeerFound)
{
// devices tap and detect each other, socket connection
set is setup.
DisplayConsoleMessage("Starting the connection ...");
}
if (eArgs.State == TriggeredConnectState.Completed)
{
DisplayConsoleMessage("Connect has been set up");
// get the socket which has been set up
_connectionSocket = eArgs.Socket;
await
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.StartSocketReadWrite();
});
}
if (eArgs.State == TriggeredConnectState.Failed)
{
DisplayConsoleMessage("Failed to set up socket
14
connection.");
}
}
private async void ConnectionRequestedEventHandler(object
sender, ConnectionRequestedEventArgs args)
{
_invitedPlayerDevice = args.PeerInformation;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()
=>
{
DisplayConsoleMessage("Another player is available on
" + args.PeerInformation.DisplayName + ", press Accept button to play a
game.");
this.AcceptButton.Visibility = Visibility.Visible;
});
}
// Start the reader and the writer to talk to the other player
device
void StartSocketReadWrite()
{
StartPlayGameButton.Visibility = Visibility.Collapsed;
InvitPlayerButton.Visibility = Visibility.Collapsed;
AcceptButton.Visibility = Visibility.Collapsed;
ProximityDeviceList.Visibility = Visibility.Collapsed;
FindPlayersButton.Visibility = Visibility.Collapsed;
_reader = new DataReader(_connectionSocket.InputStream);
_writer = new DataWriter(_connectionSocket.OutputStream);
_connectionSocketClosed = false;
ReadSocketMessage();
}
async void ReadSocketMessage()
{
try
{
uint byteCount = await _reader.LoadAsync(sizeof(uint));
if (byteCount > 0)
{
uint len = (uint)_reader.ReadUInt32();
byteCount = await _reader.LoadAsync(len);
if (byteCount > 0)
{
String message = _reader.ReadString(len);
ProcessReadMessage(message);
if (_gameEnds != true)
{
DisplayConsoleMessage("Received the move
from the other player, your turn to play.");
}
// get the move of the other player, now is my
turn to play
_turnToPlay = true;
15
ReadSocketMessage(); // waiting for the next
socket read
}
else
{
HandleSocketError("The socket reading
failed.");
}
}
else
{
HandleSocketError("The socket reading failed.");
}
}
catch (Exception e)
{
if (!_connectionSocketClosed)
{
HandleSocketError("The socket reading failed with
the reason " + e.Message);
}
}
}
private void ProcessReadMessage(String msg)
{
char[] delimiters = { '|', '\t' };
String[] elems = msg.Split(delimiters);
if (elems.Length == 1)
{
if (elems[0].Equals(_replayRequest,
StringComparison.OrdinalIgnoreCase))
{
ResetGame();
DisplayConsoleMessage("The other player has
requested to play again.");
}
else if (elems[0].Equals(_endGameRequest,
StringComparison.OrdinalIgnoreCase))
{
_gameEnds = true;
ReplayGameButton.Visibility = Visibility.Visible;
DisplayConsoleMessage("The other player has
requested to end this game.");
}
}
if (elems.Length > 1)
{
int rivalPlayer = Convert.ToInt32(elems[0]);
int pos = Convert.ToInt32(elems[1]);
MakeThePlay(rivalPlayer, pos);
if (elems.Length > 2)
{
if (elems[2].Equals(_iwin,
StringComparison.OrdinalIgnoreCase))
{
16
_gameEnds = true;
HandleGameEnds(GameEndsType.OTHERSIDEWON);
}
else if (elems[2].Equals(_gameDraw,
StringComparison.OrdinalIgnoreCase))
{
_gameEnds = true;
HandleGameEnds(GameEndsType.DRAW);
}
}
}
}
private void HandleSocketError(String errMessage)
{
DisplayConsoleMessage(errMessage);
StartPlayGameButton.Visibility = Visibility.Visible;
if (_browseSupported)
{
FindPlayersButton.Visibility = Visibility.Visible;
}
if (!_connectionSocketClosed)
{
_connectionSocketClosed = true;
_connectionSocket.Dispose();
_connectionSocket = null;
}
}
async void FindPlayers(object sender, RoutedEventArgs e)
{
DisplayConsoleMessage("Looking for other players...");
try
{
_deviceList = await PeerFinder.FindAllPeersAsync();
}
catch (Exception ex)
{
DisplayConsoleMessage("Looking for other players
encountered problem: " + ex.Message);
}
if (_deviceList.Count > 0)
{
ProximityDeviceList.Items.Clear();
for (int i = 0; i < _deviceList.Count; i++)
{
ListBoxItem lbItem = new ListBoxItem();
lbItem.Content = _deviceList[i].DisplayName;
ProximityDeviceList.Items.Add(lbItem);
}
InvitPlayerButton.Visibility = Visibility.Visible;
ProximityDeviceList.Visibility = Visibility.Visible;
DisplayConsoleMessage("Other players are available,
press the Invite A Player button to invite a player to play.");
17
}
else
{
DisplayConsoleMessage("No other player is available");
InvitPlayerButton.Visibility = Visibility.Collapsed;
ProximityDeviceList.Visibility = Visibility.Collapsed;
}
}
async void InvitePlayer(object sender, RoutedEventArgs e)
{
DisplayConsoleMessage("");
PeerInformation playerInvted = null;
try
{
// by default, select the first player
if (ProximityDeviceList.SelectedIndex == -1)
{
playerInvted = _deviceList[0];
}
else
{
playerInvted =
_deviceList[ProximityDeviceList.SelectedIndex];
}
DisplayConsoleMessage("Inviting the play on " +
playerInvted.DisplayName + " to play...");
_connectionSocket = await
PeerFinder.ConnectAsync(playerInvted);
DisplayConsoleMessage("The game is set up. Your turn to
play.");
StartSocketReadWrite();
_playerID = 1;
_turnToPlay = true;
EndGameButton.Visibility = Visibility.Visible;
}
catch (Exception err)
{
DisplayConsoleMessage("Failed to set up the game
connection with player on " + playerInvted.DisplayName + " reason: " +
err.Message);
}
}
async private void Accept(object sender, RoutedEventArgs e)
{
_playerID = 2;
_turnToPlay = false;
DisplayConsoleMessage("Setting up the game with player on "
+ _invitedPlayerDevice.DisplayName + "...");
AcceptButton.Visibility = Visibility.Collapsed;
EndGameButton.Visibility = Visibility.Visible;
try
{
_connectionSocket = await
PeerFinder.ConnectAsync(_invitedPlayerDevice);
18
DisplayConsoleMessage("The game connection was set up
successfully. The other player's turn to play.");
StartSocketReadWrite();
}
catch (Exception err)
{
DisplayConsoleMessage("Setting up game connection with
player on " + _invitedPlayerDevice.DisplayName + " failed: " +
err.Message);
}
}
private void Replay(object sender, RoutedEventArgs e)
{
ResetGame();
SendMessage(_replayRequest);
}
private void EndGame(object sender, RoutedEventArgs e)
{
_gameEnds = true;
SendMessage(_endGameRequest);
ReplayGameButton.Visibility = Visibility.Visible;
DisplayConsoleMessage("The game was stopped.");
}
private void ResetGame()
{
DisplayConsoleMessage("");
for (int ix = 0; ix < 9; ix++)
{
MakeThePlay(0, ix);
}
_gameEnds = false;
ReplayGameButton.Visibility = Visibility.Collapsed;
EndGameButton.Visibility = Visibility.Visible;
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was
reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LaunchActivatedEventArgs launchActivatedEventArgs =
e.Parameter as LaunchActivatedEventArgs;
if ((launchActivatedEventArgs != null) &&
(launchActivatedEventArgs.Arguments ==
"Windows.Networking.Proximity.PeerFinder:StreamSocket"))
{
_launchedActivatedByPeerFinder = true;
}
if (_triggeredSupported || _browseSupported)
19
{
StartPlayGameButton.Visibility = Visibility.Visible;
FindPlayersButton.Visibility = Visibility.Collapsed;
InvitPlayerButton.Visibility = Visibility.Collapsed;
ProximityDeviceList.Visibility = Visibility.Collapsed;
AcceptButton.Visibility = Visibility.Collapsed;
EndGameButton.Visibility = Visibility.Collapsed;
if (_launchedActivatedByPeerFinder)
{
DisplayConsoleMessage("This game was started by
player on a proximity device");
_launchedActivatedByPeerFinder = false;
StartPlayGame(null, null);
}
}
else
{
DisplayConsoleMessage("Proximity API is not supported
on this device. This app can not run.");
}
}
// Invoked when the main page navigates to a different scenario
protected override void
OnNavigatingFrom(NavigatingCancelEventArgs e)
{
if (_peerFinderStarted)
{
PeerFinder.TriggeredConnectionStateChanged -= new
TypedEventHandler<object,
TriggeredConnectionStateChangedEventArgs>(TriggeredConnectionStateChang
edEventHandler);
PeerFinder.ConnectionRequested -= new
TypedEventHandler<object,
ConnectionRequestedEventArgs>(ConnectionRequestedEventHandler);
PeerFinder.Stop();
if (_connectionSocket != null)
{
_connectionSocketClosed = true;
_connectionSocket.Dispose();
_connectionSocket = null;
}
_peerFinderStarted = false;
}
}
public async void DisplayConsoleMessage(string strMessage)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()
=>
{
OutputTextBlock.Text = "\n" + strMessage;
});
}
void HandleClick_Button00(object sender, RoutedEventArgs e)
20
{
HandleClick(0, sender, e);
}
void HandleClick_Button01(object sender, RoutedEventArgs e)
{
HandleClick(1, sender, e);
}
void HandleClick_Button02(object sender, RoutedEventArgs e)
{
HandleClick(2, sender, e);
}
void HandleClick_Button03(object sender, RoutedEventArgs e)
{
HandleClick(3, sender, e);
}
void HandleClick_Button04(object sender, RoutedEventArgs e)
{
HandleClick(4, sender, e);
}
void HandleClick_Button05(object sender, RoutedEventArgs e)
{
HandleClick(5, sender, e);
}
void HandleClick_Button06(object sender, RoutedEventArgs e)
{
HandleClick(6, sender, e);
}
void HandleClick_Button07(object sender, RoutedEventArgs e)
{
HandleClick(7, sender, e);
}
void HandleClick_Button08(object sender, RoutedEventArgs e)
{
HandleClick(8, sender, e);
}
void HandleClick(int btnIndex, object sender, RoutedEventArgs
e)
{
Button srcButton = sender as Button;
if ((_gameEnds == false) && (_turnToPlay == true) &&
(_grid[btnIndex] == 0))
{
MakeThePlay(_playerID, btnIndex);
GameEndsType moveResult = CheckIfGameEnds(_playerID,
btnIndex);
String msg = _playerID.ToString();
msg += "|";
msg += btnIndex.ToString();
if (moveResult == GameEndsType.IWON)
21
{
_gameEnds = true;
msg += "|";
msg += _iwin;
}
else if (moveResult == GameEndsType.DRAW)
{
_gameEnds = true;
msg += "|";
msg += _gameDraw;
}
SendMessage(msg);
if (_gameEnds != true)
{
DisplayConsoleMessage("The move was sent to the
other player, now is the other player's turn to play.");
}
_turnToPlay = false;
if (_gameEnds == true)
{
HandleGameEnds(moveResult);
}
}
}
private void MakeThePlay(int _playerID, int btnIndex)
{
if (_playerID == 1)
{
_buttons[btnIndex].Background = new ImageBrush
{ ImageSource = new BitmapImage(new Uri("ms-
appx:///Assets/ximage.png")) };
}
else if (_playerID == 2)
{
_buttons[btnIndex].Background = new ImageBrush
{ ImageSource = new BitmapImage(new Uri("ms-
appx:///Assets/oimage.png")) };
}
else if (_playerID == 0)
{
_buttons[btnIndex].Background = new ImageBrush
{ ImageSource = new BitmapImage(new Uri("ms-
appx:///Assets/initialimage.png")) };
}
_grid[btnIndex] = _playerID;
}
private GameEndsType CheckIfGameEnds(int player, int btnIdx)
{
GameEndsType res = GameEndsType.CONTINUE;
switch (btnIdx)
{
case 0:
22
if (((_grid[1] == _grid[0]) && (_grid[2] ==
_grid[0])) ||
((_grid[3] == _grid[0]) && (_grid[6] ==
_grid[0])) ||
((_grid[4] == _grid[0]) && (_grid[8] ==
_grid[0])))
{
res = GameEndsType.IWON;
}
break;
case 1:
if (((_grid[0] == _grid[1]) && (_grid[2] ==
_grid[1])) ||
((_grid[4] == _grid[1]) && (_grid[7] ==
_grid[1])))
{
res = GameEndsType.IWON;
}
break;
case 2:
if (((_grid[0] == _grid[2]) && (_grid[1] ==
_grid[2])) ||
((_grid[5] == _grid[2]) && (_grid[8] ==
_grid[2])) ||
((_grid[4] == _grid[2]) && (_grid[6] ==
_grid[2])))
{
res = GameEndsType.IWON;
}
break;
case 3:
if (((_grid[4] == _grid[3]) && (_grid[5] ==
_grid[3])) ||
((_grid[0] == _grid[3]) && (_grid[6] ==
_grid[3])))
{
res = GameEndsType.IWON;
}
break;
case 4:
if(((_grid[1] == _grid[4]) && (_grid[7] ==
_grid[4])) ||
((_grid[3] == _grid[4]) && (_grid[5] ==
_grid[4]))||
((_grid[0] == _grid[4]) && (_grid[8] ==
_grid[4])) ||
((_grid[2] == _grid[4]) && (_grid[6] ==
_grid[4])))
{
res = GameEndsType.IWON;
}
break;
case 5:
if (((_grid[3] == _grid[5]) && (_grid[4] ==
_grid[5])) ||
((_grid[2] == _grid[5]) && (_grid[8] ==
_grid[5])))
23
{
res = GameEndsType.IWON;
}
break;
case 6:
if (((_grid[7] == _grid[6]) && (_grid[8] ==
_grid[6])) ||
((_grid[0] == _grid[6]) && (_grid[3] ==
_grid[6])) ||
((_grid[4] == _grid[6]) && (_grid[2] ==
_grid[6])))
{
res = GameEndsType.IWON;
}
break;
case 7:
if (((_grid[6] == _grid[7]) && (_grid[8] ==
_grid[7])) ||
((_grid[1] == _grid[7]) && (_grid[4] ==
_grid[7])))
{
res = GameEndsType.IWON;
}
break;
case 8:
if (((_grid[6] == _grid[8]) && (_grid[7] ==
_grid[8])) ||
((_grid[2] == _grid[8]) && (_grid[5] ==
_grid[8])) ||
((_grid[4] == _grid[8]) && (_grid[0] ==
_grid[8])))
{
res = GameEndsType.IWON;
}
break;
}
if (res != GameEndsType.IWON)
{
bool gameTies = true;
for (int ix = 0; ix < 9; ix++)
{
if (_grid[ix] == 0)
{
gameTies = false;
}
}
if (gameTies == true)
{
res = GameEndsType.DRAW;
}
}
return res;
}
private void HandleGameEnds(GameEndsType type)
{
switch (type)
24
{
case GameEndsType.IWON:
DisplayConsoleMessage("I Won!");
break;
case GameEndsType.OTHERSIDEWON:
DisplayConsoleMessage("The Other Side Won!");
break;
case GameEndsType.DRAW:
DisplayConsoleMessage("A Draw!");
break;
}
ReplayGameButton.Visibility = Visibility.Visible;
}
async private void SendMessage(String msg)
{
DisplayConsoleMessage("");
String message = msg;
if (!_connectionSocketClosed)
{
if (message.Length > 0)
{
try
{
uint len = _writer.MeasureString(message);
_writer.WriteUInt32(len);
_writer.WriteString(message);
uint byteCount = await _writer.StoreAsync();
if (byteCount > 0)
{
}
else
{
HandleSocketError("The communication with
the other player interrupted.");
}
}
catch (Exception err)
{
if (!_connectionSocketClosed)
{
HandleSocketError("Communication with the
other player error: " + err.Message);
}
}
}
}
else
{
HandleSocketError("The communication with the other
player device is closed.");
}
}
}
public enum GameEndsType
25
{
IWON,
OTHERSIDEWON,
DRAW,
CONTINUE
};
}
Figure 9 The MainPage.xaml.cs file (**)
3.7 Run the App
After you finish the changes described in the above sections and you successfully build the solution,
you can deploy the app on two tablets that support NFC and/or Wi-Fi Direct peer browsing. You can
play the game in action on the two devices (Figure 10).
Figure 10 The Tic-Tac-Toe game start screen
26
4 Summary
On Intel Atom processor-based tablets running Windows 8, by using Windows Proximity API, we can
create connections between two devices with a simple tap gesture, or through Wi-Fi Direct peer
browsing. The connected devices can then exchange data with each other without connecting to a
network. The Windows Proximity API enables application developers to enhance their apps and
create multi-player application experiences.
About the Author
Miao Wei works as a software engineer with Intel Corporation’s Software and Services Group.
Notices
INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO
LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY
RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND
CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND
INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL
PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER
INTELLECTUAL PROPERTY RIGHT.
UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR
INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE
A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.
Intel may make changes to specifications and product descriptions at any time, without notice.
Designers must not rely on the absence or characteristics of any features or instructions marked
"reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility
whatsoever for conflicts or incompatibilities arising from future changes to them. The information
here is subject to change without notice. Do not finalize a design with this information.
27
The products described in this document may contain design defects or errors known as errata
which may cause the product to deviate from published specifications. Current characterized errata
are available on request.
Contact your local Intel sales office or your distributor to obtain the latest specifications and before
placing your product order.
Copies of documents which have an order number and are referenced in this document, or other
Intel literature, may be obtained by calling 1-800-548-4725, or go
to: http://www.intel.com/design/literature.htm
Intel, the Intel logo and Atom are trademarks of Intel Corporation in the U.S. and other countries.
*Other names and brands may be claimed as the property of others
**This sample source code is released under the Intel Sample Source Code License Agreement
Copyright© 2012 Intel Corporation. All rights reserved.
28
Optimization Notice
Optimization Notice
Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations
that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSE3 instruction sets
and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any
optimization on microprocessors not manufactured by Intel.
Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain
optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the
applicable product User and Reference Guides for more information regarding the specific instruction sets
covered by this notice.
Notice revision #20110804