Click here to Skip to main content
15,894,122 members
Articles / Multimedia / DirectX

Multiplayer Snake in 3D using C# and Managed DirectX

Rate me:
Please Sign up or sign in to vote.
4.00/5 (7 votes)
12 Aug 20042 min read 136.9K   3.2K   42  
A new version of the old game.
using System; 
using System.Drawing; 
using System.Collections; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Data;
using System.IO; 
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using dinput = Microsoft.DirectX.DirectInput; 

namespace Snake3D
{ 
	public class Game : System.Windows.Forms.Form 
	{
		// private variables for Direct3D and DirectInput
		dinput.Device input;
        Device device; 
		Microsoft.DirectX.Direct3D.Font font12;
		Microsoft.DirectX.Direct3D.Font font36;
		Microsoft.DirectX.Direct3D.Font font24;

		// constants to switch between options
		const bool FullScreen = true;
		const bool AntiAlias = true;
		const bool ShowFPS = true;

		float ElapsedTime, UpdateTime, AppTime;		
        private float speed = 0.25f;

		Intro intro;
		Arena arena;
		EventKey Escape, Return;
		Level level;
		Player[] players = new Player[2];
		
		Random rnd = new Random(Environment.TickCount);

		// variables used to determine the state of the game and other things
		bool IntroPlaying = true;
		bool GameFinished = false;
		Vector3 ZoomInPoint;
		float z = 0f;
		public static Vector3 CamPos = new Vector3(40,40,14);
		Vector3 UpVector;

		static void Main() 
		{
			using (Game frm = new Game())
			{
				frm.Show();				
				Application.Run(frm);				
			}
		}		

		public Game()
		{
			this.Size = new Size(800,600);
			this.StartPosition = FormStartPosition.CenterScreen; 
			this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
			this.InitializeInput();
			this.InitializeGraphics();
		}

		private void InitializeInput()
		{
			// standard DirectInput Initialisation procedure
			Microsoft.DirectX.DirectInput.CooperativeLevelFlags coopFlags;
			coopFlags = Microsoft.DirectX.DirectInput.CooperativeLevelFlags.NonExclusive;
			coopFlags |= Microsoft.DirectX.DirectInput.CooperativeLevelFlags.Background;			
			input = new Microsoft.DirectX.DirectInput.Device(dinput.SystemGuid.Keyboard);
			input.SetCooperativeLevel(this,coopFlags);
			input.Acquire();

			// Hook and Create EventKeys
			Escape = new EventKey(dinput.Key.Escape,input);
			Escape.KeyUp += new EventHandler(Escape_KeyUp);
			Return = new EventKey(dinput.Key.Return,input);
			Return.KeyUp += new EventHandler(Return_KeyUp);
		}

		private void GetInput()
		{										
			// Get and Process the input
			dinput.KeyboardState kstate = null;
			kstate = input.GetCurrentKeyboardState();
			Escape.Process();
			Return.Process();
			for ( int i = 0; i < players.Length; i ++)
				players[i].CheckKeys(kstate);
		}
     
		protected void InitializeGraphics() 
		{ 
			Cursor.Hide();
			// Standard Direct3D Initialisation procedure
			PresentParameters pres = new PresentParameters(); 
			pres.Windowed = ! FullScreen; 
			pres.SwapEffect = SwapEffect.Discard; 
			pres.EnableAutoDepthStencil = true ; 
			pres.AutoDepthStencilFormat = DepthFormat.D16;
			if ( FullScreen)
			{
				pres.BackBufferCount = 1;
				pres.BackBufferFormat = Format.X8R8G8B8;
				pres.BackBufferWidth = 1280;
				pres.BackBufferHeight = 1024;
			}			
			int b,Q = 0;
			if ( AntiAlias)
			{
				for ( int i = 0; i < (int) MultiSampleType.SixteenSamples; i ++)
				{
					if ( Manager.CheckDeviceMultiSampleType(0,DeviceType.Hardware,Manager.Adapters[0].CurrentDisplayMode.Format,! FullScreen,(MultiSampleType) i,out b,out Q) )			
						pres.MultiSample = (MultiSampleType) i;			
				}
				pres.MultiSampleQuality = Q-1;		
			}
			device = new Device(0, DeviceType.Hardware, this , CreateFlags.HardwareVertexProcessing | CreateFlags.PureDevice , 
				pres); 
			device.RenderState.CullMode = Cull.None; 
			font12 = new Microsoft.DirectX.Direct3D.Font(device,new System.Drawing.Font("Arial",12));
			font24 = new Microsoft.DirectX.Direct3D.Font(device,new System.Drawing.Font("Arial",24));
			font36 = new Microsoft.DirectX.Direct3D.Font(device,new System.Drawing.Font("Arial",36,FontStyle.Bold));

			device.Transform.Projection = Matrix.PerspectiveFovLH( (float) Math.PI /2f,(float)this.Width/(float) this.Height ,1f,200f);

			level = new Level( (Levels) rnd.Next(Level.numLevels) );
			
			// Create the players
			players[0] = new Player(device,dinput.Key.W,dinput.Key.S,
				dinput.Key.A,dinput.Key.D,TextureLoader.FromFile(device,@"..\..\tex3.bmp"),level,"Player 1",0);
			players[1] = new Player(device,dinput.Key.UpArrow,dinput.Key.DownArrow,
				dinput.Key.LeftArrow,dinput.Key.RightArrow,TextureLoader.FromFile(device,@"..\..\tex1.bmp"),level,"Player 2",1);
			
			// Hook the event fired when a player crashes to something
			for ( int i = 0; i < players.Length; i ++)
				players[i].onLost += new lost(Playerlost);            
			
			// Texture filtering prevents textures to look bad when Magnified or Minified
			// Comment these lines out if your card does not support this function
			// To check wether your device supports Texture Filtering use the Manager class
			device.SamplerState[0].MagFilter = TextureFilter.Anisotropic;
			device.SamplerState[0].MinFilter = TextureFilter.Anisotropic;

			// Play the intro
			intro = new Intro(device,"Snake 3D");
			intro.Start(DXUtil.Timer(DirectXTimer.GetAbsoluteTime) );
			intro.Ready += new Finished(IntroFinished);					

			arena = new Arena(device,TextureLoader.FromFile(device,@"..\..\tex4.bmp"),level);
		}
		
		protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
		{			
			// Overided the onPaint event of the form

			GetInput();			
			
			
			if ( UpdateTime > speed )
			{
				// Enough time has passed the snakes are ready to be updated
				for ( int i = 0; i < players.Length; i ++)
					players[i].Update();
				UpdateTime = 0;
			}

			// Check to see if the intro should still be rendered.
			if ( IntroPlaying)
				RenderIntro();
			else
			{
				// Nope, the game
				if ( ! GameFinished)
				UpdateTime += ElapsedTime;
				Render();
			}

			// Invalidate the form, this will cause the onPaint method to be called Again
			this.Invalidate();
		}		

		protected void Render() 
		{ 
			// Don't render if the device isn't created or already disposed
			if ( device == null) return;
			if ( device.Disposed ) return;
			ElapsedTime = DXUtil.Timer(DirectXTimer.GetElapsedTime);
			AppTime = DXUtil.Timer(DirectXTimer.GetAbsoluteTime);
			
			// Check other tutorials for information about the Device object
			device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black , 1.0F, 0); 
			device.BeginScene();
			
			for ( int i = 0; i < players.Length; i ++)
				font24.DrawText(null,string.Format("{0} : {1}",players[i].Name,players[i].Score),new Rectangle(3,24*i+3,0,0),DrawTextFormat.NoClip,Color.Blue);
			
			arena.Render();
			device.VertexFormat = CustomVertex.PositionTextured.Format;

			Material mat = new Material();
			mat.Diffuse = Color.White;
			mat.Ambient = Color.White;			
			device.Material = mat;
			device.RenderState.Ambient = Color.DarkGray;
			device.RenderState.CullMode = Cull.None;

			for ( int i = 0; i < players.Length; i ++)
				players[i].Render(); 			
			
			if ( GameFinished)
			{
				// One of the players has crashed draw text to inform the user
				font36.DrawText(null,"The game has finished",new Rectangle(this.Width / 2 - 275,this.Height / 2 - 60,0,0),DrawTextFormat.NoClip,Color.Chartreuse);
				font24.DrawText(null,"Press Escape to quit, Enter to start again",new Rectangle(this.Width / 2 - 300,this.Height / 2,0,0),DrawTextFormat.NoClip,Color.Silver);
				// Zoom in on where the crash happened
				if ( z < 0.90f)
				z += 0.01f;
				device.Transform.View = Matrix.LookAtLH( Vector3.Lerp(CamPos,ZoomInPoint,z),ZoomInPoint,UpVector );
			} 
				
			device.EndScene();
			device.Present();						
			
		}

		protected void RenderIntro()
		{
			if ( device == null) return;
			ElapsedTime = DXUtil.Timer(DirectXTimer.GetElapsedTime);
			AppTime = DXUtil.Timer(DirectXTimer.GetAbsoluteTime);
			device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black , 1.0F, 0); 
			device.BeginScene();
			intro.Render(AppTime);
			device.EndScene();
			device.Present();	
		}

		private void IntroFinished(bool finished)
		{
			// The intro is finished take the camera above the arena
			IntroPlaying = !finished;
			device.Transform.View = Matrix.LookAtLH( CamPos,Arena.ArenaCenter,new Vector3(0,1,0) );
		}

		private void Escape_KeyUp(object sender, EventArgs e)
		{
			if ( IntroPlaying )
				intro.Stop();
			else
			{
				// Dispose Devices
				if ( input != null)
				{
					input.Unacquire();
					input.Dispose();
					input = null;
				}
				if ( device != null)
				{
					device.Dispose();
					device = null;
				}
				this.Close();	
			}
		}

		private void Playerlost(Player player, Vector3 v)
		{			
			for ( int i = 0; i < players.Length; i ++)
				if ( players[i] != player) players[i].Score ++;
			ZoomInPoint = v;
			z = 0f;
			
			// This calculates the UpVector for the ViewMatrix, not really working
			float dx = CamPos.X - ZoomInPoint.X;
			float dy = CamPos.X - ZoomInPoint.X;
			float dz = CamPos.X - ZoomInPoint.X;
			float dxz = (float) Math.Sqrt(dx*dx+dz*dz);
			float alfa = (float) ( Math.PI - Math.Atan(dy/dxz) );
			float beta = (float) Math.Atan(dz/dx);			
			UpVector = new Vector3( (float) Math.Cos(beta) * dxz,(float) Math.Cos(alfa),(float) Math.Sin(beta)*dxz );
			UpVector.Normalize();
			GameFinished = true;
		}

		private void Return_KeyUp(object sender, EventArgs e)
		{
			if ( GameFinished )
				RestartGame();
		}

		private void RestartGame()
		{			
			// Clear the level and assign a new level to it.
			level.Reset( (Levels) rnd.Next(Level.numLevels) );
			// Clear the trails of the player and reset their position
			for ( int i = 0; i < players.Length; i ++)
				players[i].Reset(level);						
			// Inform the arena about where to draw obstacles
			arena.ResetObstacles(level);
			GameFinished = false;
			// Reset the camera position
			device.Transform.View = Matrix.LookAtLH( CamPos,Arena.ArenaCenter,new Vector3(0,0,1) );
		}

//		private Vector3 v3Lerp(Vector3 v1,Vector3 v2,float l)
//		{
//			return new Vector3( v1.X * l + (1f-l) * v2.X, v1.Y * l + (1f-l) * v2.Y, v1.Z * l + (1f-l) * v2.Z);
//		}


	}
} 


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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions