Click here to Skip to main content
15,896,063 members
Articles / Multimedia / DirectX

DirectX Board Game Engine

Rate me:
Please Sign up or sign in to vote.
4.64/5 (19 votes)
29 Nov 2007CPOL11 min read 91.5K   1.7K   50  
An article on how to create a generic engine for board games such as Checkers or Chess
using System;
using System.Collections.Generic;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Bornander.Games.Direct3D;

namespace Bornander.Games.BoardGame.Direct3D
{
    /// <summary>
    /// This class is a renderer for a <code>CheckersLogic</code> object.
    /// </summary>
    class VisualBoard
    {
        #region Private members

        private IBoardGameLogic gameLogic;
        private IBoardGameModelRepository boardGameModelRepository;
        private Square selectedSquare = Square.Negative;
        
        // This is used to determine which one is the grabbed piece so that rendering
        // of that one can be skipped since that is handeled bu the GamePanel.
        private Square currentPieceOrigin = Square.Negative;

        #endregion

        public VisualBoard(IBoardGameLogic gameLogic, IBoardGameModelRepository boardGameModelRepository) 
        {
            this.gameLogic = gameLogic;
            this.boardGameModelRepository = boardGameModelRepository;
        }

        public void Render(Device device)
        {
            for (int row = 0; row < gameLogic.Rows; ++row)
            {
                for (int column = 0; column < gameLogic.Columns; ++column)
                {
                    Square currentSquare = new Square(row, column);

                    Model boardSquare = boardGameModelRepository.GetBoardSquareModel(currentSquare);
                    boardSquare.Position = new Vector3((float)column, 0.0f, (float)row);
                    boardSquare.Selected = currentSquare.Equals(selectedSquare);
                    boardSquare.Render(device);

                    // Check that the current piece isn't grabbed by the mouse, because in that case we
                    // don't render it.
                    if (!currentPieceOrigin.Equals(currentSquare))
                    {
                        // Check which kind of model we need to render, move our "template" to the
                        // right position and render it there.
                        Model pieceModel = boardGameModelRepository.GetBoardPieceModel(gameLogic[currentSquare]);
                        if (pieceModel != null)
                        {
                            pieceModel.Position = new Vector3((float)column, 0.0f, (float)row);
                            pieceModel.Render(device);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// This method allows to check for "MouseOver" on Models in 3D space.
        /// </summary>
        public bool GetMouseOverBlockModel(Device device, int mouseX, int mouseY, out Square square, List<Square> highlightIfHit)
        {
            selectedSquare = Square.Negative;
            square = new Square();
            bool foundMatch = false;
            float closestMatch = int.MaxValue;
            for (int row = 0; row < gameLogic.Rows; ++row)
            {
                for (int column = 0; column < gameLogic.Columns; ++column)
                {
                    Square currentSquare = new Square(row, column);
                    Model boardSquare = boardGameModelRepository.GetBoardSquareModel(currentSquare);
                    boardSquare.Position = new Vector3((float)column, 0.0f, (float)row);

                    Vector3 near = new Vector3(mouseX, mouseY, 0.0f);
                    Vector3 far = new Vector3(mouseX, mouseY, 1.0f);

                    // Unproject a vector from the screen X,Y space into 3D space using the World matrix of the mesh we're checking.
                    near.Unproject(device.Viewport, device.Transform.Projection, device.Transform.View, boardSquare.World);
                    far.Unproject(device.Viewport, device.Transform.Projection, device.Transform.View, boardSquare.World);
                    far.Subtract(near);

                    // Find the closes match of all blocks, that is the one the mouse is over.
                    IntersectInformation closestIntersection;
                    if (boardSquare.Mesh.Intersect(near, far, out closestIntersection) && closestIntersection.Dist < closestMatch)
                    {
                        closestMatch = closestIntersection.Dist;
                        square = new Square(row, column);

                        // If a list of squares is passed in we're over one of those squares we highlight that 
                        // this is used to indicated valid moves to the user
                        if (highlightIfHit != null)
                        {
                            foreach (Square highlightSquare in highlightIfHit)
                            {
                                if (highlightSquare.Equals(square))
                                {
                                    selectedSquare = new Square(row, column);
                                }
                            }
                        }

                        foundMatch = true;
                    }
                }
            }
            return foundMatch;
        }


        /// <summary>
        /// Picks up a piece, simply marking the currentPieceLocaiton with the location of the picked piece's
        /// square so that it not gets rendered by this class.
        /// </summary>
        /// <param name="pieceLocation"></param>
        /// <returns></returns>
        public Model PickUpPiece(Square pieceLocation)
        {
            if (pieceLocation.Row < 0 || pieceLocation.Row >= gameLogic.Rows || pieceLocation.Column < 0 || pieceLocation.Column >= gameLogic.Columns)
                return null;
                
            if (gameLogic[pieceLocation] > 0) // TODO: Fix constant for this magic value
            {
                currentPieceOrigin = pieceLocation;
                return boardGameModelRepository.GetBoardPieceModel(gameLogic[pieceLocation]);
            }
            else
            {
                return null;
            }
        }

        public void DropPiece()
        {
            currentPieceOrigin = Square.Negative;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
Article videos
Oakmead Apps Android Games

21 Feb 2014: Best VB.NET Article of January 2014 - Second Prize
18 Oct 2013: Best VB.NET article of September 2013
23 Jun 2012: Best C++ article of May 2012
20 Apr 2012: Best VB.NET article of March 2012
22 Feb 2010: Best overall article of January 2010
22 Feb 2010: Best C# article of January 2010

Comments and Discussions