Silverlight 8-Ball
- Posted: Sep 19, 2007 at 2:03 PM
- 2,781 Views
- 20 Comments
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
| In this article, I will discuss how I have built a 2 player 8-Ball game in Silverlight (play here). I will explain how I used the Expression tools to design the graphics and various .NET techniques to enable user control and game animation. | |
|
Difficulty: Intermediate
Time Required:
6-10 hours
Cost: Free
Software: Visual Studio 2008 Beta 2,
Silverlight 1.1 Alpha Refresh,
Expression Design,
Expression Blend 2 Preview
Hardware:
Download: Download
|
|
In this article, I will discuss how I have built a 2 player 8-Ball game in Silverlight (play here). I will explain how I used the Expression tools to design the graphics and various .NET techniques to enable user control and game animation.
The following components must be implemented to complete this game:
Before I continue, I'd like to point out that I did not originate all of the code and XAML for this solution. I came up with the idea of creating a Silverlight 8-Ball game after stumbling upon a 2-D “bouncing bubble” animation here: http://www.bubblemark.com/ created by Alexey Gavrilov.
The original intent for this solution was to compare the performance of the bouncing bubbles across various platforms. I found this very useful, but was personally interested in making something fun and interactive out of it.
The following tools were used to implement this solution:
If you'd like to learn more about game development, I found a number Coding4Fun posts under Gaming helpful. In particular, the 2D Game Primer by "ZMan" gives a good overview of the basics (e.g. GameLoop, Sprites, etc.)
I am not much of a graphic designer. In fact, I've spent the majority of my career designing and implementing line of business apps for enterprise customers (typically not a very graphically intensive task). So I believe it is a decent testament to the effectiveness of the Microsoft Expression suite that I could, in short order, create the graphics for this game.
I used Expression Design to build the graphics of the pool table. Modeling my design after the standard dimension of a 9 foot billiards table, I drew the main structure as a rectangle with rounded corners. I drew a smaller green rectangle centered over it for the playing surface (of course following standard dimensions). Next, I created 6 black circles placed one level behind the playing surface to give the impression of pockets. Lastly, I added the wood texture to the outside rectangle (which was super easy as there is a set of wood textures available by default), and drew a simple rounded rectangle beneath the table to serve as a status bar.
I also created the graphics for my pool stick using Expression Design. This consisted of a few tapered polygons and 2 half circles for the butt and tip.
After exporting the associated XAML, I imported each element into my project. From here, I could view and refine the design through a scaled down visual editor or the associated XAML.
Lastly, I inspected the XAML markup for the pool ball I reused from Alexey's Bubblemark solution. I needed to understand the properties of this object so that I could modify the color of the balls later on.
The root of Silverlight8Ball is a Storyboard (x:Name=”GameLoop”) that acts as a triggering mechanism for determining ball velocity, direction, and redirection due to collision. Whereas some Storyboards are implemented with predetermined paths and timelines directly in XAML, the action taken at each tick must be determined dynamically in this game. So we simply set the duration to 00:00:0, and handle the Completed event to trigger our positioning logic. This design effectively creates what game developers call a “Game Loop” that triggers continuous processing to determine application state.
The resulting “GameLoop_Completedhandler” becomes the root processing agent for all of the game's ball movement logic.
C#
1: void GameLoop_Completed(object sender, EventArgs e)
2: {
3: switch (m_ActionState)
4: {
5: case m_ActionStates.BallsMoving:
6:
7: // prep
8: List<Ball> removeList = new List<Ball>();
9: bool someBallsAreMoving = false;
10:
11: // move each ball
12: foreach (Ball ball in m_GameBalls)
13: {
14: ball.Move();
15: if (ball.InPocket) removeList.Add(ball);
16: else if (ball.IsMoving) someBallsAreMoving = true;
17: }
18:
19: // store balls sunk on shot and update ui
20: foreach (Ball ball in removeList)
21: {
22: sunkBalls.Add(ball);
23: RemoveBall(ball);
24: }
25:
26: if (someBallsAreMoving)
27: {
28: // update vectors for ball collisions
29: for (int i = 0; i < m_GameBalls.Count; i++)
30: {
31: for (int j = i + 1; j < m_GameBalls.Count; j++)
32: {
33: m_GameBalls[i].DoCollide(m_GameBalls[j]);
34: }
35: }
36: }
37: else
38: {
39: // determine shot results
40: ShotResults results = EvaluateShot();
41:
42: // apply state and ui changes
43: UpdateGameState(results);
44: }
45:
46: break;
47: }
48:
49: // restart the storyboard
50: if (m_IsRunning)
51: {
52: GameLoop.Begin();
53: }
54:
55: }
VB
1: Private Sub GameLoop_Completed(ByVal sender As Object, ByVal e As EventArgs)
2:
3: Select Case m_ActionState
4:
5: Case m_ActionStates.BallsMoving
6:
7: ' prep
8: Dim removeList As List(Of Ball) = New List(Of Ball)()
9: Dim someBallsAreMoving As Boolean = False
10:
11: ' move each ball
12: For Each ball As Ball In m_GameBalls
13: ball.Move()
14: If ball.InPocket Then
15: removeList.Add(ball)
16: ElseIf ball.IsMoving Then
17: someBallsAreMoving = True
18: End If
19: Next ball
20:
21: ' store balls sunk on shot and update ui
22: For Each ball As Ball In removeList
23: sunkBalls.Add(ball)
24: RemoveBall(ball)
25: Next ball
26:
27: If someBallsAreMoving Then
28: ' update vectors for ball collisions
29: For i As Integer = 0 To m_GameBalls.Count - 1
30: For j As Integer = i + 1 To m_GameBalls.Count - 1
31: m_GameBalls(i).DoCollide(m_GameBalls(j))
32: Next j
33: Next i
34: Else
35: ' determine shot results
36: Dim results As ShotResults = EvaluateShot()
37:
38: ' apply state and ui changes
39: UpdateGameState(results)
40: End If
41:
42: End Select
43:
44: ' restart the storyboard
45: If m_IsRunning Then
46: GameLoop.Begin()
47: End If
48:
49: End Sub
The above code snippets demonstrate the full functionality of our GameLoop. However, the primary animation and collision logic are handled by "ball.Move();" and "m_GameBalls[i].DoCollide(m_GameBalls[j]);" lines (in C# example). The Move() function of each ball applies its current vector (i.e. x and y "velocities") to determine its next position and updates the ball's UI element coordinates. Once the ball is advanced, the DoCollide function checks if two balls have collided. If so, their vectors are adjusted accordingly.
C#
1: public bool DoCollide(Ball b)
2: {
3: // calculate some vectors
4: double dx = this._x - b._x;
5: double dy = this._y - b._y;
6: double dvx = this._vx - b._vx;
7: double dvy = this._vy - b._vy;
8: double distance2 = dx * dx + dy * dy;
9:
10: if (Math.Abs(dx) > this._d || Math.Abs(dy) > this._d)
11: return false;
12: if (distance2 > this._d2)
13: return false;
14:
15: // make absolutely elastic collision
16: double mag = dvx * dx + dvy * dy;
17:
18: // test that balls move towards each other
19: if (mag > 0)
20: return false;
21:
22: mag /= distance2;
23:
24: double delta_vx = dx * mag;
25: double delta_vy = dy * mag;
26:
27: this._vx -= delta_vx;
28: this._vy -= delta_vy;
29:
30: b._vx += delta_vx;
31: b._vy += delta_vy;
32:
33: return true;
34: }
VB
1: Public Function DoCollide(ByVal b As Ball) As Boolean
2: ' calculate some vectors
3: Dim dx As Double = Me._x - b._x
4: Dim dy As Double = Me._y - b._y
5: Dim dvx As Double = Me._vx - b._vx
6: Dim dvy As Double = Me._vy - b._vy
7: Dim distance2 As Double = dx * dx + dy * dy
8:
9: If Math.Abs(dx) > Me._d OrElse Math.Abs(dy) > Me._d Then
10: Return False
11: End If
12: If distance2 > Me._d2 Then
13: Return False
14: End If
15:
16: ' make absolutely elastic collision
17: Dim mag As Double = dvx * dx + dvy * dy
18:
19: ' test that balls move towards each other
20: If mag > 0 Then
21: Return False
22: End If
23:
24: mag /= distance2
25:
26: Dim delta_vx As Double = dx * mag
27: Dim delta_vy As Double = dy * mag
28:
29: Me._vx -= delta_vx
30: Me._vy -= delta_vy
31:
32: b._vx += delta_vx
33: b._vy += delta_vy
34:
35: Return True
36: End Function
While the GameLoop Storyboard does a good job at managing ball movement, I didn't think it was the best option for handling pool stick and q-ball control. During times of user control (stick aiming and q-ball "ball in hand"), I chose to use mouse movement and click events to determine object position and angle. I created my own enumeration to manage switching between ball movement and user control states. The rest involves some simple statistics and Silverlight XAML transformations.
During a scratch, the MouseMove event allows the affect of moving the q-ball for placement. During aiming, it allows the user to rotate the poolstick around the q-ball.
C#
1: void Page_MouseMove(object sender, MouseEventArgs e)
2: {
3:
4: m_MousePoint = e.GetPosition(this);
5:
6: switch (m_ActionState)
7: {
8: case m_ActionStates.Scratch:
9: m_QBall.MoveAbsolute(m_MousePoint.X, m_MousePoint.Y);
10: break;
11: case m_ActionStates.Aiming:
12: m_PoolStick.Rotate(m_MousePoint.X, m_MousePoint.Y, m_QBall.BallCenterX, m_QBall.BallCenterY);
13: break;
14: }
15:
16: }
VB
1: Private Sub Page_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
2:
3: m_MousePoint = e.GetPosition(Me)
4:
5: Select Case m_ActionState
6: Case m_ActionStates.Scratch
7: m_QBall.MoveAbsolute(m_MousePoint.X, m_MousePoint.Y)
8: Case m_ActionStates.Aiming
9: m_PoolStick.Rotate(m_MousePoint.X, m_MousePoint.Y, m_QBall.BallCenterX, m_QBall.BallCenterY)
10: End Select
11:
12: End Sub
The Silverlight RotateTranform is very powerful in this case. It allows me to simply rotate the pool stick object around a center point (the center of the q-ball) using a relative angle between the mouse point and the ball.
C#
1: public void Rotate (double mouseX, double mouseY, double ballX, double ballY)
2: {
3: double vx = ballX - mouseX;
4: double vy = ballY - mouseY;
5:
6: radians = Math.Atan2(vy, vx);
7: double angle = radians * (180/Math.PI);
8:
9: rootCanvas.RenderTransform = new RotateTransform
10: {
11: CenterX = Model.stickBuffer,
12: CenterY = Model.stickHeight/2,
13: Angle = angle
14: };
15:
16: }
VB
1: Public Sub Rotate(ByVal mouseX As Double, ByVal mouseY As Double, ByVal ballX As Double, ByVal ballY As Double)
2: Dim vx As Double = ballX - mouseX
3: Dim vy As Double = ballY - mouseY
4:
5: radians = Math.Atan2(vy, vx)
6: Dim angle As Double = radians * (180 / Math.PI)
7:
8: Dim transform As RotateTransform = New RotateTransform()
9: transform.CenterX = Model.stickBuffer
10: transform.CenterY = Model.stickHeight / 2
11: transform.Angle = angle
12:
13: rootCanvas.RenderTransform = transform
14:
15: End Sub
Based on the ActionState, the mouse up and mouse down events manage user interaction during power adjustment and q-ball placement.
C#
1: void Page_MouseLeftButtonUp(object sender, MouseEventArgs e)
2: {
3: if (this.m_ActionState == m_ActionStates.AdjustingPower)
4: {
5: m_QBall.Strike(m_PoolStick.power, m_PoolStick.radians);
6:
7: m_PoolStick.StopPowerMovement();
8: this.Children.Remove(m_PoolStick);
9: this.m_ActionState = m_ActionStates.BallsMoving;
10: }
11: }
12:
13: void Page_MouseLeftButtonDown(object sender, MouseEventArgs e)
14: {
15: switch (m_ActionState)
16: {
17: case m_ActionStates.Aiming:
18: m_PoolStick.StartPowerMovement();
19: this.m_ActionState = m_ActionStates.AdjustingPower;
20: break;
21: case m_ActionStates.Scratch:
22: ResetPoolStick();
23: m_ActionState = m_ActionStates.Aiming;
24: break;
25: }
26: }
VB
1: Private Sub Page_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseEventArgs)
2: If Me.m_ActionState = m_ActionStates.AdjustingPower Then
3: m_QBall.Strike(m_PoolStick.power, m_PoolStick.radians)
4:
5: m_PoolStick.StopPowerMovement()
6: Me.Children.Remove(m_PoolStick)
7: Me.m_ActionState = m_ActionStates.BallsMoving
8: End If
9: End Sub
10:
11: Private Sub Page_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseEventArgs)
12: Select Case m_ActionState
13: Case m_ActionStates.Aiming
14: m_PoolStick.StartPowerMovement()
15: Me.m_ActionState = m_ActionStates.AdjustingPower
16: Case m_ActionStates.Scratch
17: ResetPoolStick()
18: m_ActionState = m_ActionStates.Aiming
19: End Select
20: End Sub
The final component to this application is the "business rules" behind the game. A game of 8-Ball is governed by the results of each player's shot. Whether that shot ends the game or whose shot it is next is based on what balls are sunk.
I created another enumeration for ShotResults which defines each potential outcome: GoAgain, NextPlayer, Scratch, ScratchOnEight, PrematureEightBall, and Win.
C#
1: private void UpdateGameState(ShotResults results)
2: {
3: string resultText = "";
4:
5: switch (results)
6: {
7: // update game states
8: case ShotResults.PrematureEightBall:
9: case ShotResults.ScratchOnEight:
10: resultText = ProcessGameEnd(false, results);
11: m_ActionState = m_ActionStates.GameOver;
12: break;
13: case ShotResults.NextPlayer:
14: resultText = "Player looses turn";
15: ChangeCurrentPlayer();
16: ResetPoolStick();
17: m_ActionState = m_ActionStates.Aiming;
18: break;
19: case ShotResults.GoAgain:
20: resultText = "Nice job. Go again.";
21: ResetPoolStick();
22: m_ActionState = m_ActionStates.Aiming;
23: break;
24: case ShotResults.Scratch:
25: resultText = "Scratch. Player looses turn.";
26: ChangeCurrentPlayer();
27: AddBall(m_QBall);
28: m_ActionState = m_ActionStates.Scratch;
29: break;
30: case ShotResults.Win:
31: resultText = ProcessGameEnd(true, results);
32: m_ActionState = m_ActionStates.GameOver;
33: break;
34: }
35:
36: sunkBalls.Clear();
37:
38: this.Text_Status.Text = resultText;
39: }
VB
1: Private Sub UpdateGameState(ByVal results As ShotResults)
2: Dim resultText As String = ""
3:
4: Select Case results
5: ' update game states
6: Case ShotResults.PrematureEightBall, ShotResults.ScratchOnEight
7: resultText = ProcessGameEnd(False, results)
8: m_ActionState = m_ActionStates.GameOver
9: Case ShotResults.NextPlayer
10: resultText = "Player looses turn"
11: ChangeCurrentPlayer()
12: ResetPoolStick()
13: m_ActionState = m_ActionStates.Aiming
14: Case ShotResults.GoAgain
15: resultText = "Nice job. Go again."
16: ResetPoolStick()
17: m_ActionState = m_ActionStates.Aiming
18: Case ShotResults.Scratch
19: resultText = "Scratch. Player looses turn."
20: ChangeCurrentPlayer()
21: AddBall(m_QBall)
22: m_ActionState = m_ActionStates.Scratch
23: Case ShotResults.Win
24: resultText = ProcessGameEnd(True, results)
25: m_ActionState = m_ActionStates.GameOver
26: End Select
27:
28: sunkBalls.Clear()
29:
30: Me.Text_Status.Text = resultText
31: End Sub
This was a fun and educational exercise for me (always a good combination). I learned enough about graphics design to at least fear it less, especially with the use of the Expression suite. It also opened my perspective of Silverlight to understand how it will empower us to implement more rich interactive applications. The most interesting part for me, however, was the change in perspective I had to take in implementing an "always on" game. As simple as a game of 8-ball is, it definitely required an adjustment in thinking from the event-driven model of programming I'm accustomed to using in my every day work.
If you have any questions about this solution or have suggestions for improvement, feel free to contact me at jpetersen@claritycon.com
Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums,
or
Contact Us and let us know.
Follow the Discussion
Oops, something didn't work.
What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in. You need to be signed in to Channel 9 to use this feature.What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in and view them all on your notifications page.sign up for email notifications?
Posted in SilverlightShow Highlights
thanks, good toturial, thou I don't understand all of it, I learned some stuff
I was wondering (as i am very new to programming),
if it would be possible to do this project using visual studio 2005, silverlight and the expression components as all of these i have only just acquired.
This was a fun and educational exercise for me (always a good combination). I learned enough about graphics design to at least fear it less, especially with the use of the Expression suite. It also opened my perspective of Silverlight to understand how it will empower us to implement more rich interactive applications. The most interesting part for me, however, was the change in perspective I had to take in implementing an "always on" game. As simple as a game of 8-ball is, it definitely required an adjustment in thinking from the event-driven model of programming I'm accustomed to using in my every day work.
"Player looses turn" should be "Player loses turn".
I too am accustomed to linear, event driven business-oriented programming, mainly with databases and VB. Nice to see I'm not the only square trying to broaden his horizons
Have downloaded the alpa 1.1 on windows xp service pack 2 with Windows Explorer on multiple machines ( and have restarted ). Keeps saying that I need to download Silverlight.
That was pretty awesome! Can you help me with code for a simple game of tic-tac-toe or mabey tell me where I might find code for the game?
Thank you very much.
Jeanie
A Tic tac toe game is pretty easy Jeanie if you think about it, you have a fixed win state that you can check after each turn.
What would be really neat is if you used Ink to draw the X and O. I think I have a new idea ...
Steve: You may need to replace the JS files with the newer silverlight 1.1 files. I've run into this problem too. That is why Silverlight 1.1 is deemed alpha (not production ready) and has a bunch of disclaimers, when it gets updated, it may break stuff since it is still that new.
Title says it all
My plan is to create small walkthrough how you can create your own Silverlight
Title says it all
My plan is to create small walkthrough how you can create your own Silverlight
Uno de los Blogs a los que estoy suscrito es el de Janne Mattila quien siempre tiene cosas muy interesantes
Very Interesting... good article
i am unable to open this silverlight_8ball.sln in VisualBasic2008ExpressEdition why?
In this article, I will discuss how I have built RetroCommand, a Silverlight 2.0 Beta version of a classic
hi, thanks for explaining your program
I have been bugging my head about how to implement ball collision but it seems your solution is a good one.
I understand the method: 'doCollide(param)' but would love to know why you did what you did in that method i.e. can u please exlain the formula.
cheers
В этой статье я опишу версию классической аркадной игры RetroCommand, созданную мной с применением Silverlight
@ge-force This was with SL 1.1 which is javascript only. This example would have to be updated to Silverlight 3.0 or 4.0 to get on Visual Studio 2010. That requires shifting it from JS to SL.
Is there any way to right this just with Expression Blend, Silverlight 3, and Visual Studio 2010 Professional?
Remove this comment
Remove this thread
close