|
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;
namespace Replicator {
/// <summary>
/// A single Robot
/// </summary>
public partial class Robot : Form {
#region Classes/Enums
/// <summary>
/// Holding the avaible brain states for the robot
/// </summary>
public enum eBrain {
Idle,
Following,
Attacking,
CursorCatched,
Wait
}
#endregion
#region Properties
/// <summary>
/// Current X Position (centered)
/// </summary>
public float X {
get { return m_X; }
set {
m_X = value;
//Invoke is required, as the thread is different
Invoke(new EventHandler(delegate { Location = new Point((int) m_X - Width/2, Location.Y); }));
}
}
/// <summary>
/// Current Y Position (bottomed ;P)
/// </summary>
public float Y {
get { return m_Y; }
set {
m_Y = value;
//Invoke is required, as the thread is different
Invoke(new EventHandler(delegate { Location = new Point(Location.X, (int) m_Y - Height); }));
}
}
/// <summary>
/// Returns the position as a Point
/// </summary>
public Point Pos {
get { return new Point((int) Math.Round(m_X), (int) Math.Round(m_Y)); }
}
/// <summary>
/// Contains the current brain state
/// Causes redraw when changed
/// </summary>
public eBrain State {
get { return m_State; }
set {
if (value != m_State) {
m_State = value;
Redraw();
}
}
}
#endregion
private static readonly Random m_Random = new Random();
private bool m_OnGround = false;
private eBrain m_State = eBrain.Wait;
private float m_VelX = 0;
private float m_VelY = 0;
private float m_X;
private float m_Y;
public Robot() {
InitializeComponent();
SetStyle(ControlStyles.UserPaint, true); //We draw it ourself
}
public Robot(int x, int y) : this() {
m_X = x;
m_Y = y;
m_VelX = (float) m_Random.NextDouble()*3;
m_VelY = (float) m_Random.NextDouble()*3;
}
/// <summary>
/// Redraws the form
/// (Crossthread call)
/// </summary>
public void Redraw() {
Invoke(new EventHandler(delegate { Refresh(); }));
}
/// <summary>
/// Draws the robot
/// </summary>
/// <param name="sender">-</param>
/// <param name="e">-</param>
private void Robot_Paint(object sender, PaintEventArgs e) {
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.None; //Border is drawn without antialiasing
g.FillEllipse(Brushes.Black, 1, 1, Width - 2, Height - 2); //border
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawEllipse(Pens.White, 2, 2, Width - 4, Height - 4); //Ring
Brush middle;
switch (State) {
default:
case eBrain.Idle:
middle = Brushes.Gray;
break;
case eBrain.Following:
middle = Brushes.Blue;
break;
case eBrain.Attacking:
middle = Brushes.Red;
break;
case eBrain.CursorCatched:
middle = Brushes.White;
break;
case eBrain.Wait:
middle = Brushes.Black;
break;
}
g.FillEllipse(middle, 12, 12, Width - 24, Height - 24); //Middle part (Brain indicator)
}
/// <summary>
/// Called when the user clicks with the mouse on one of the robots
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Robot_MouseClick(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Right) {
Environment.Exit(0);
}
//Set state to inactive
State = eBrain.Wait;
m_VelX = (float) m_Random.NextDouble()*3;
m_VelY = (float) m_Random.NextDouble()*3;
}
/// <summary>
/// Called when the form is shown
/// Starts the robot thread
/// </summary>
/// <param name="sender">-</param>
/// <param name="e"-></param>
private void Robot_Shown(object sender, EventArgs e) {
X = m_X;
Y = m_Y;
//Start our acting thread
Thread a = new Thread(Loop);
a.Start();
}
/// <summary>
/// The Mainloop of the robot
/// Calls Act() and Think()
/// </summary>
public void Loop() {
while (!Disposing) {
Think();
Act();
Thread.Sleep(33);
}
}
/// <summary>
/// Represents the acting part of the robot
/// Or in other words: the physics/mech part
/// </summary>
public void Act() {
//Friction..
m_VelX *= 0.95f;
m_VelY *= 0.95f;
//Stop movement complete if its to low, otherwise there'll be some sort of "endless jumping"
if (Math.Abs(m_VelX) < 0.5f) {
m_VelX = 0;
}
if (Math.Abs(m_VelY) < 0.5f) {
m_VelY = 0;
}
m_OnGround = Y >= Screen.PrimaryScreen.WorkingArea.Height;
if (m_OnGround) //Is on ground
{
if (m_VelY > 0) {
m_VelY = -m_VelY; //"Bounce"
}
}
else {
m_VelY += 2f; //Apply Gravity!
}
if (X < 0) //Out of screen: left
{
m_VelX = Math.Abs(m_VelX);
}
else if (X > Screen.PrimaryScreen.WorkingArea.Width) //Out of screen: right
{
m_VelX = -Math.Abs(m_VelX);
}
X += m_VelX;
Y += m_VelY;
}
/// <summary>
/// Represents the thinking part of the robot
/// In this part the robot decides what to do
/// </summary>
public void Think() {
double dist = GetDistance(Cursor.Position, Pos);
switch (State) {
case eBrain.Idle: //The idle part decides what to do next
{
if (dist <= 30) {
State = eBrain.CursorCatched;
}
else if (dist <= 600) {
State = eBrain.Attacking;
}
else if (dist <= 900) {
State = eBrain.Following;
}
Stand();
}
break;
case eBrain.Attacking: //Jump to the target
{
if (dist < 30 || dist > 600) {
State = eBrain.Idle;
}
else {
Walk(Cursor.Position.X - (int) X);
Jump(Cursor.Position.Y - (int) Y);
}
}
break;
case eBrain.Following: //Just walk to the target
{
if (dist <= 600 || dist > 900) {
State = eBrain.Idle;
}
else {
Walk(Math.Min(Cursor.Position.X - (int) X, 20));
}
}
break;
case eBrain.CursorCatched: //Cought!
{
//We use the average to give the user the change to move the cursor
int avgX = (Cursor.Position.X + (int) X) / 2;
int avgY = (Cursor.Position.Y + (int) Y) / 2;
Cursor.Position = new Point(avgX, avgY);
X = avgX;
Y = avgY;
if (m_Random.NextDouble() < 0.001) {
State = eBrain.Wait; //drop of, hanging around there long enough
}
}
break;
case eBrain.Wait: //Inactive
{
if (m_Random.NextDouble() < 0.010) {
State = eBrain.Idle; //Reactivate
}
}
break;
}
}
#region Robot functions
/// <summary>
/// Makes the robot slow down and stand still
/// </summary>
public void Stand() {
Walk(-(int) Math.Round(m_VelX*10));
}
/// <summary>
/// Makes the robot walk
/// </summary>
/// <param name="x">Units to walk</param>
public void Walk(int x) {
m_VelX += x/100f;
}
/// <summary>
/// Makes the robot jump
/// </summary>
/// <param name="y">Height</param>
public void Jump(int y) {
if (m_OnGround && y < 0) {
m_VelY += y/10;
}
}
#endregion
#region Helpers
/// <summary>
/// Returns the distance between two Points
/// </summary>
/// <param name="a">Point A</param>
/// <param name="b">Point B</param>
/// <returns>Distance</returns>
private static double GetDistance(Point a, Point b) {
int relX = a.X - b.X;
int relY = a.Y - b.Y;
return Math.Sqrt((relX*relX) + (relY*relY));
}
#endregion
}
}
|
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.