Jump to content

  • Log In with Google      Sign In   
  • Create Account

Basfreak

Member Since 14 Mar 2012
Offline Last Active Jun 24 2012 03:18 PM

Topics I've Started

[C#] Ingame context menu

14 March 2012 - 09:27 AM

Hey guys,

I'm working on a RTS like game and just implemented context menus. When you click on a tree or anything a context menu will popup with the available options. It works, but I'd like to know if it could be done nicer / better.

I've got a ContextMenu class where the drawing and events are handled, a Tree class to show how it's used and a part of Game1.cs:

ContextMenu.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace MyGame
{
	public class ContextMenu
	{
		public static int minMenuWidth = 64;
		public static int minMenuHeight = 64;
		public static Sprite contextMenuBackground;
		public static Sprite contextMenuBorder;

		private Game1 theGame;
		private Vector2 position;
		private bool visible;
		private GameObject target;

		private List<ContextMenuItem> itemList = new List<ContextMenuItem>();

		private int width = minMenuWidth;
		private int height = minMenuHeight;

		public ContextMenu(Game1 g, List<ContextMenuItem> items)
		{
			theGame = g;
			foreach (ContextMenuItem item in items)
			{
				if (9 * item.getText().Length > width)
					width = 9 * item.getText().Length;
			}
			itemList = items;
			target = null;
		}

		public ContextMenu(Game1 g)
		{
			theGame = g;
			target = null;
		}

		public bool onClick(MouseState mouseState)
		{
			Point mousePosition = new Point(mouseState.X, mouseState.Y);
			Rectangle menuHitBox = new Rectangle((int)position.X, (int)position.Y, width, height);
			if (menuHitBox.Contains(mousePosition))
			{
				for (int i = 0; i < itemList.Count; i++)
				{
					ContextMenuItem item = itemList[i];
					Rectangle hitBox = new Rectangle((int)position.X + 10, (int)position.Y + 8 + 12 * (i), width - 20, 12);
					if (hitBox.Contains(mousePosition))
					{
						item.onClick(target);
						return true;
					}
				}
				return true;
			}
			return false;
		}

		public void setTarget(GameObject o)
		{
			target = o;
		}

		public GameObject getTarget()
		{
			return target;
		}

		public void updateHovers(MouseState mouseState)
		{
			Point mousePosition = new Point(mouseState.X, mouseState.Y);
			Rectangle menuHitBox = new Rectangle((int)position.X, (int)position.Y, width, height);
			if (menuHitBox.Contains(mousePosition))
			{
				for (int i = 0; i < itemList.Count; i++)
				{
					ContextMenuItem item = itemList[i];
					item.setHovering(false);
					Rectangle hitBox = new Rectangle((int)position.X + 10, (int)position.Y + 8 + 12 * (i), width - 20, 12);
					if (hitBox.Contains(mousePosition))
					{
						item.setHovering(true);
					}
				}
			}
		}

		public Rectangle getHitBox()
		{
			return new Rectangle((int)position.X, (int)position.Y, width, height);
		}

		public bool isVisible()
		{
			return visible;
		}

		public void addContextMenuItem(ContextMenuItem item)
		{
			itemList.Add(item);
		}

		public ContextMenuItem getContextMenuItem(int i)
		{
			if (i >= 0 && i < itemList.Count)
				return itemList[i];
			else
				return null;					
		}

		public void draw()
		{
			if (!visible || itemList.Count == 0)
				return;

			// bg
			for (int y = 0; y < ((float)height / 64); y++)
			{
				for (int x = 0; x < ((float)width / 64); x++)
				{
					if (y * 64 + 64 <= height && x * 64 + 64 <= width)
					{
						theGame.spriteBatch.Draw(contextMenuBackground.getTexture(), new Rectangle((int)position.X + 64 * x, (int)position.Y + 64 * y, 64, 64), contextMenuBackground.getSourceRectangle(0), Color.White);
					}
					else
					{
						Rectangle sourceRect = contextMenuBackground.getSourceRectangle(0);
						sourceRect.Width = width - x * 64;
						sourceRect.Height = height - y * 64;
						theGame.spriteBatch.Draw(contextMenuBackground.getTexture(), new Rectangle((int)position.X + 64 * x, (int)position.Y + 64 * y, sourceRect.Width, sourceRect.Height), sourceRect, Color.White);
					}
				}
			}

			// left top
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X, (int)position.Y, 8, 8), contextMenuBorder.getSourceRectangle(0), Color.White);
			// right top
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X + width - 8, (int)position.Y, 8, 8), contextMenuBorder.getSourceRectangle(1), Color.White);

			// left
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X, (int)position.Y + 8, 8, height - 16), contextMenuBorder.getSourceRectangle(4), Color.White);
			// right
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X + width - 8, (int)position.Y + 8, 8, height - 16), contextMenuBorder.getSourceRectangle(5), Color.White);

			// top
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X + 8, (int)position.Y, width - 16, 8), contextMenuBorder.getSourceRectangle(7), Color.White);
			// bottom
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X + 8, (int)position.Y + height - 8, width - 16, 8), contextMenuBorder.getSourceRectangle(6), Color.White);

			// left bottom
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X, (int)position.Y + height - 8, 8, 8), contextMenuBorder.getSourceRectangle(2), Color.White);
			// right bottom
			theGame.spriteBatch.Draw(contextMenuBorder.getTexture(), new Rectangle((int)position.X + width - 8, (int)position.Y + height - 8, 8, 8), contextMenuBorder.getSourceRectangle(3), Color.White);

			// Draw the items:
			for (int i = 0; i < itemList.Count; i++)
			{
				ContextMenuItem item = itemList[i];
				if (item.isHovering())
				{
					Rectangle hitBox = new Rectangle((int)position.X + 9, (int)position.Y + 9 + 12 * (i), width - 18, 12);
					theGame.spriteBatch.Draw(theGame.dummy, new Vector2((float)hitBox.X, (float)hitBox.Y), null, new Color(255, 255, 255) * 0.3f, 0f, Vector2.Zero, new Vector2(hitBox.Width, hitBox.Height), SpriteEffects.None, 0f);
				}
				theGame.drawShadedString(theGame.spriteBatch, theGame.defaultFont, item.getText(), new Vector2((int)position.X + 10, (int)position.Y + 8 + 12*(i)), Color.White, 1.0f, new Vector2(1.0f, 1.0f), Color.Black);
			}
		}

		public void setPosition(int x, int y)
		{
			position.X = (float) x;
			position.Y = (float) y;
		}

		public void setWidth(int w)
		{
			width = w;
		}

		public void setHeight(int h)
		{
			height = h;
		}

		public void show()
		{
			visible = true;
		}

		public void hide()
		{
			visible = false;
		}
	}
}

Tree.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace MyGame
{
	class Tree : ResourceObject
	{
		public static int treeWidth = 16;
		public static int treeHeight = 16;
		public static double maxTreeHealth = 100.0;
		public static ContextMenu contextMenu;

		private Sprite sprite;
		private Random rand;

		public Tree(Game1 g, Level l, Sprite s, int x, int y)
		{
			// bla bla
		}

		public override void tick(GameTime gameTime)
		{
		}

		public override void draw()
		{
			theGame.spriteBatch.Draw(sprite.getTexture(), new Rectangle((int)position.X, (int)position.Y, width, height), sprite.getSourceRectangle(), Color.White);
		}

		public override ContextMenu getContextMenu()
		{
			return contextMenu;
		}
	}
}


The action that will happen when an item is clicked is defined in Game1.cs:

List<ContextMenuItem> menuItemlist = new List<ContextMenuItem>();
			menuItemlist.Add(new ContextMenuItem("Chop tree", delegate(GameObject o)
			{
				if (o != null)
				{
					if (selectedAnts.Count > 0)
					{
						foreach (Ant selectedAnt in selectedAnts)
						{
							Point walkTarget = new Point(o.getX(), o.getY());
							selectedAnt.setTarget(o);
							selectedAnt.walkNear(walkTarget);
							contextMenu.hide();
						}
					}
				}
			}));
			Tree.contextMenu = new ContextMenu(this, menuItemlist);

Is this a good way of doing this? Using delegates as parameters for menu items?

Another idea I had was the following:
Create a IContextMenu interface with 2 methods: getContextMenuItems() and contextMenuOnClick(ContextMenuItem clickedItem) a right-clickable gameobject will than implement this interface. The drawing and handling of the clicks are than handled within Game1.cs.

Thanks Posted Image
Bas

PARTNERS