I'm making a 2D game engine called Clixel over on GitHub. The problem I have relates to two classes, ClxMouse and ClxButton. In it I have a mouse class - the code for that can be viewed here.
ClxMouse
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace org.clixel
{
public class ClxMouse : ClxSprite
{
private MouseState _curmouse, _lastmouse;
public int Sensitivity = 3;
public bool Lock = true;
public Vector2 Change
{
get
{
return new Vector2(_curmouse.X - _lastmouse.X, _curmouse.Y - _lastmouse.Y);
}
}
private int _scrollwheel;
public int ScrollWheel
{
get
{
return _scrollwheel;
}
}
public bool LeftDown
{
get
{
if (_curmouse.LeftButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public bool RightDown
{
get
{
if (_curmouse.RightButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public bool MiddleDown
{
get
{
if (_curmouse.MiddleButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public bool LeftPressed
{
get
{
if (_curmouse.LeftButton == ButtonState.Pressed && _lastmouse.LeftButton == ButtonState.Released)
return true;
else
return false;
}
}
public bool RightPressed
{
get
{
if (_curmouse.RightButton == ButtonState.Pressed && _lastmouse.RightButton == ButtonState.Released)
return true;
else
return false;
}
}
public bool MiddlePressed
{
get
{
if (_curmouse.MiddleButton == ButtonState.Pressed && _lastmouse.MiddleButton == ButtonState.Released)
return true;
else
return false;
}
}
public bool LeftReleased
{
get
{
if (_curmouse.LeftButton == ButtonState.Released && _lastmouse.LeftButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public bool RightReleased
{
get
{
if (_curmouse.RightButton == ButtonState.Released && _lastmouse.RightButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public bool MiddleReleased
{
get
{
if (_curmouse.MiddleButton == ButtonState.Released && _lastmouse.MiddleButton == ButtonState.Pressed)
return true;
else
return false;
}
}
public MouseState CurMouse
{
get
{
return _curmouse;
}
}
public MouseState LastMouse
{
get
{
return _lastmouse;
}
}
public ClxMouse()
: base(ClxG.Textures.Default.Cursor)
{
_curmouse = Mouse.GetState();
_lastmouse = _curmouse;
CollisionBox = new Rectangle(ClxG.Screen.Center.X, ClxG.Screen.Center.Y, Texture.Width, Texture.Height);
this.Solid = false;
DefaultPosition = new Vector2(CollisionBox.X, CollisionBox.Y);
Mouse.SetPosition(CollisionBox.X, CollisionBox.Y);
}
public ClxMouse(Texture2D _texture)
: base(_texture)
{
_curmouse = Mouse.GetState();
_lastmouse = _curmouse;
CollisionBox = new Rectangle(ClxG.Screen.Center.X, ClxG.Screen.Center.Y, Texture.Width, Texture.Height);
DefaultPosition = new Vector2(CollisionBox.X, CollisionBox.Y);
}
public override void Update()
{
_lastmouse = _curmouse;
_curmouse = Mouse.GetState();
if (_curmouse != _lastmouse)
{
if (ClxG.Game.IsActive)
{
_scrollwheel = _curmouse.ScrollWheelValue;
Velocity = new Vector2(Change.X / Sensitivity, Change.Y / Sensitivity);
if (Lock)
Mouse.SetPosition(ClxG.Screen.Center.X, ClxG.Screen.Center.Y);
_curmouse = Mouse.GetState();
}
base.Update();
}
}
public override void Draw(SpriteBatch _sb)
{
base.Draw(_sb);
}
}
}
ClxButton
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace org.clixel
{
public class ClxButton : ClxSprite
{
/// <summary>
/// The color when the mouse is over the button
/// </summary>
public Color HoverColor;
/// <summary>
/// The color when the color is being clicked
/// </summary>
public Color ClickColor;
/// <summary>
/// The color when the button is inactive
/// </summary>
public Color InactiveColor;
/// <summary>
/// The color when the button is active
/// </summary>
public Color ActiveColor;
/// <summary>
/// The color after the button has been clicked.
/// </summary>
public Color ClickedColor;
/// <summary>
/// The text to be displayed on the button, set to "" if no text is needed.
/// </summary>
public string Text;
/// <summary>
/// The ClxText object to be displayed.
/// </summary>
public ClxText TextRender;
/// <summary>
/// The ClxState that should be ResetAndShow() when the button is clicked.
/// </summary>
public ClxState ClickState;
/// <summary>
/// Collision check to make sure onCollide() only runs once per frame,
/// since only the mouse needs to be collision checked.
/// </summary>
private bool _runonce = false;
/// <summary>
/// Gets a value indicating whether this instance is colliding.
/// </summary>
/// <value>
/// <c>true</c> if this instance is colliding; otherwise, <c>false</c>.
/// </value>
public bool IsColliding
{
get
{
return _runonce;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ClxButton"/> class.
/// </summary>
public ClxButton()
: base(ClxG.Textures.Default.Button)
{
HoverColor = Color.Red;
ClickColor = Color.Blue;
InactiveColor = Color.Gray;
ActiveColor = Color.White;
ClickedColor = Color.Yellow;
Text = Name + ID + " Unset!";
TextRender = new ClxText();
TextRender.Text = Text;
TextRender.TextPadding = new Vector2(5, 5);
ClickState = null;
CollideObjects(ClxG.Mouse);
}
/// <summary>
/// Initializes a new instance of the <see cref="ClxButton"/> class.
/// </summary>
/// <param name="_texture">The button texture.</param>
public ClxButton(Texture2D _texture)
: base(_texture)
{
HoverColor = Color.Red;
ClickColor = Color.Blue;
InactiveColor = Color.Gray;
ActiveColor = Color.White;
ClickedColor = Color.Yellow;
Texture = _texture;
Text = Name + ID;
TextRender = new ClxText();
TextRender.Name = this.Name + ".TextRender";
TextRender.Text = Text;
TextRender.TextPadding = new Vector2(5, 5);
TextRender.Reset();
ClickState = null;
CollideObjects(ClxG.Mouse);
}
/// <summary>
/// Draws the debug information, run from ClxG.DrawDebug unless manual control is assumed.
/// </summary>
/// <param name="_sb">SpriteBatch used for drawing.</param>
public override void DrawDebug(SpriteBatch _sb)
{
_runonce = false;
TextRender.DrawDebug(_sb);
_sb.Draw(Texture, ActualRectangle, new Rectangle(0, 0, Texture.Width, Texture.Height), DebugColor, Rotation, Origin, Flip, Layer);
_sb.Draw(ClxG.Textures.Default.DebugBG, new Rectangle(ActualRectangle.X - DebugLineWidth, ActualRectangle.Y - DebugLineWidth, ActualRectangle.Width + DebugLineWidth * 2, ActualRectangle.Height + DebugLineWidth * 2), new Rectangle(0, 0, ClxG.Textures.Default.DebugBG.Width, ClxG.Textures.Default.DebugBG.Height), DebugOutline, Rotation, Origin, Flip, Layer - 0.1f);
_sb.Draw(ClxG.Textures.Default.DebugBG, ActualRectangle, new Rectangle(0, 0, ClxG.Textures.Default.DebugBG.Width, ClxG.Textures.Default.DebugBG.Height), DebugBGColor, Rotation, Origin, Flip, Layer - 0.01f);
}
/// <summary>
/// Draws using the SpriteBatch, run from ClxG.Draw unless manual control is assumed.
/// </summary>
/// <param name="_sb">SpriteBatch used for drawing.</param>
public override void Draw(SpriteBatch _sb)
{
_runonce = false;
TextRender.Draw(_sb);
if (Visible)
if (Debug)
{
DrawDebug(_sb);
}
else
_sb.Draw(Texture, ActualRectangle, new Rectangle(0, 0, Texture.Width, Texture.Height), Color, Rotation, Origin, Flip, Layer);
}
/// <summary>
/// Updates this instance.
/// </summary>
public override void Update()
{
if (this.Color != ActiveColor)
this.Color = ActiveColor;
TextRender.Layer = this.Layer + 0.03f;
TextRender.Text = Text;
TextRender.Scale = .5f;
TextRender.Name = this.Name + ".TextRender";
TextRender.Origin = new Vector2(TextRender.CollisionBox.Center.X, TextRender.CollisionBox.Center.Y);
TextRender.Center(this);
TextRender.Update();
this.CollisionBox.Width = (int)(TextRender.CollisionBox.Width * TextRender.Scale) + (int)(TextRender.TextPadding.X * 2);
this.CollisionBox.Height = (int)(TextRender.CollisionBox.Height * TextRender.Scale) + (int)(TextRender.TextPadding.Y * 2);
base.Update();
}
/// <summary>
/// Collide event, takes the colliding object to call it's proper collision code.
/// You'd want to use something like if(typeof(collider) == typeof(ClxObject)
/// </summary>
/// <param name="collider">The colliding object.</param>
public override void onCollide(ClxObject collider)
{
if (!_runonce)
{
_runonce = true;
UpdateEvents();
base.onCollide(collider);
}
}
/// <summary>
/// Updates the mouse based events.
/// </summary>
public void UpdateEvents()
{
onHover();
if (ClxG.Mouse.LeftReleased)
{
onLeftReleased();
return;
}
if (ClxG.Mouse.RightReleased)
{
onRightReleased();
return;
}
if (ClxG.Mouse.MiddleReleased)
{
onMiddleReleased();
return;
}
if (ClxG.Mouse.LeftPressed)
{
onLeftClicked();
return;
}
if (ClxG.Mouse.RightPressed)
{
onRightClicked();
return;
}
if (ClxG.Mouse.MiddlePressed)
{
onMiddleClicked();
return;
}
if (ClxG.Mouse.LeftDown)
{
onLeftClick();
return;
}
if (ClxG.Mouse.RightDown)
{
onRightClick();
return;
}
if (ClxG.Mouse.MiddleDown)
{
onMiddleClick();
return;
}
}
/// <summary>
/// Shows the state of the click.
/// </summary>
public void ShowClickState()
{
if (ClickState != null)
{
ClickState.ResetAndShow();
}
}
/// <summary>
/// Hover event
/// </summary>
virtual public void onHover()
{
this.Color = HoverColor;
}
/// <summary>
/// Left click event
/// </summary>
virtual public void onLeftClick()
{
this.Color = ClickColor;
}
/// <summary>
/// Right click event
/// </summary>
virtual public void onRightClick()
{
}
/// <summary>
/// Middle click event
/// </summary>
virtual public void onMiddleClick()
{
}
/// <summary>
/// Left click event, called once per click
/// </summary>
virtual public void onLeftClicked()
{
ShowClickState();
}
/// <summary>
/// Right click event, called once per click
/// </summary>
virtual public void onRightClicked()
{
this.Reset();
}
/// <summary>
/// Middle click event, called once per click
/// </summary>
virtual public void onMiddleClicked()
{
}
/// <summary>
/// Ons the left released.
/// </summary>
virtual public void onLeftReleased()
{
this.Color = ClickedColor;
}
virtual public void onRightReleased()
{
}
virtual public void onMiddleReleased()
{
}
}
}
The issue I have is that I have all these have event styled methods, especially in ClxButton with all the onLeftClick, onRightClick, etc, etc.
Is there a better way for me to handle these events to be a lot more easier for a programmer to use? I was looking at normal events on some other sites, (I'd post them but I need more rep.) and didn't really see a good way to implement delegate events into my framework. I'm not really sure how these events work, could someone possibly lay out how these events are processed for me?
TL:DR
* Is there a better way to handle events like this?
* Are events a viable solution to this problem?
Thanks in advance for any help.