0

Here is the code that I have so far for my game. I am wanting to create an AI character (BlackBall) that will follow the player (WhiteBall) when they are a certain distance away. I have no idea where to start to get this working but it will be a main part of my game so it is essential.

using System;
using System.Collections.Generic;
using System.Linq;
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 PickUpTheCrew
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    SpriteFont TitleFont;


    private Vector2 playerPos = Vector2.Zero;
    Vector2 BlackBallPos;
    Vector2 position, velocity;
    Vector2 scorePos;
    Vector2 saved;
    private KeyboardState keyboardState;
    private KeyboardState prevKeyboardState;
    private bool canMove = true;

    int score;

    //Textures for background, player and sharks.
    Texture2D BlackBallTexture;
    Texture2D BlackBallTexture2;
    Texture2D BlueBallTexture;
    Texture2D GreenBallTexture;
    Texture2D OrangeBallTexture;
    Texture2D PinkBallTexture;
    Texture2D RedBallTexture;
    Texture2D WhiteBallTexture;
    Texture2D YellowBallTexture;

    Rectangle BlackBallRectangle;
    Rectangle BlackBallRectangle2;
    Rectangle BlueBallRectangle;
    Rectangle GreenBallRectangle;
    Rectangle OrangeBallRectangle;
    Rectangle PinkBallRectangle;
    Rectangle RedBallRectangle;
    Rectangle WhiteBallRectangle;
    Rectangle YellowBallRectangle;

    Sprite mainPlayer;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {

        score = 0;

        playerPos = new Vector2(this.GraphicsDevice.Viewport.Width / 2,
                       this.GraphicsDevice.Viewport.Height * 0.25f);
        BlackBallPos = new Vector2(this.GraphicsDevice.Viewport.Width / 2,
                       this.GraphicsDevice.Viewport.Height * 0.75f);

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>

    private Texture2D BackgroundTexture;
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(Gra`enter code here`phicsDevice);

        TitleFont = Content.Load<SpriteFont>("TitleFont");
        WhiteBallTexture = Content.Load<Texture2D>("WhiteBall");

        //mainPlayer = new Sprite(Content.Load<Texture2D>("WhiteBall"), new Rectangle((int)(playerPos.X - WhiteBallTexture.Width / 2),
        //(int)(playerPos.Y - WhiteBallTexture.Height / 2), WhiteBallTexture.Width, WhiteBallTexture.Height));

        BackgroundTexture = Content.Load<Texture2D>("Background");
        BlackBallTexture = Content.Load<Texture2D>("BlackBall");
        BlackBallTexture2 = Content.Load<Texture2D>("BlackBall");
        BlueBallTexture = Content.Load<Texture2D>("BlueBall");
        GreenBallTexture = Content.Load<Texture2D>("GreenBall");
        OrangeBallTexture = Content.Load<Texture2D>("OrangeBall");
        PinkBallTexture = Content.Load<Texture2D>("PinkBall");
        RedBallTexture = Content.Load<Texture2D>("RedBall");
        YellowBallTexture = Content.Load<Texture2D>("YellowBall");

        WhiteBallRectangle = new Rectangle(100, 100, 25,25);
        BlackBallRectangle = new Rectangle(150, 300, 25,25);
        BlackBallRectangle2 = new Rectangle(500, 400, 25, 25);
        BlueBallRectangle = new Rectangle(500, 150, 25, 25);
        GreenBallRectangle = new Rectangle(100, 500, 25, 25);
        OrangeBallRectangle = new Rectangle(180, 200, 25, 25);
        PinkBallRectangle = new Rectangle(260, 260, 25, 25);
        RedBallRectangle = new Rectangle(300, 450, 25, 25);
        YellowBallRectangle = new Rectangle(550, 300, 25, 25);

        scorePos.X = 575;
        scorePos.Y = 450;
        saved.X = 0;
        saved.Y = 50;
        /*
        WhiteBallRectangle = new Rectangle((int)(playerPos.X - WhiteBallTexture.Width / 2),
        (int)(playerPos.Y - WhiteBallTexture.Height / 2), WhiteBallTexture.Width, WhiteBallTexture.Height);
        BlackBallRectangle = new Rectangle((int)(BlackBallPos.X - BlackBallTexture.Width / 2),
        (int)(BlackBallPos.Y - BlackBallTexture.Height / 2), BlackBallTexture.Width, BlackBallTexture.Height);
        */
        /*
        WhiteBallRectangle; = new Rectangle((int)(playerPos.X - WhiteBallTexture.Width / 2),
        (int)(playerPos.Y - WhiteBallTexture.Height / 2), WhiteBallTexture.Width, WhiteBallTexture.Height);
        BlackBallRectangle; = new Rectangle ((int)(BlackBallPos.X - BlackBallTexture.Width / 2),
        (int)(BlackBallPos.Y - BlackBallTexture.Height / 2), BlackBallTexture.Width, BlackBallTexture.Height);
         */

    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
        //mainPlayer.Update();

        prevKeyboardState = Keyboard.GetState();
        keyboardState = Keyboard.GetState();

        if (canMove)
        {
            if (keyboardState.IsKeyDown(Keys.Up) && prevKeyboardState.IsKeyDown(Keys.Up))
                WhiteBallRectangle.Y = WhiteBallRectangle.Y - 1;
           //playerPos -= new Vector2(0, 4);
            else if (keyboardState.IsKeyDown(Keys.Left) && prevKeyboardState.IsKeyDown(Keys.Left))
                WhiteBallRectangle.X = WhiteBallRectangle.X - 1;
            //playerPos -= new Vector2(4, 0);
            else if (keyboardState.IsKeyDown(Keys.Down) && prevKeyboardState.IsKeyDown(Keys.Down))
                WhiteBallRectangle.Y = WhiteBallRectangle.Y + 1;
            //playerPos += new Vector2(0, 4);
            else if (keyboardState.IsKeyDown(Keys.Right) && prevKeyboardState.IsKeyDown(Keys.Right))
                WhiteBallRectangle.X = WhiteBallRectangle.X + 1;
               //playerPos += new Vector2(4, 0);

                if (keyboardState.IsKeyDown(Keys.Up) && keyboardState.IsKeyDown(Keys.Left))
                {

                    WhiteBallRectangle.X = WhiteBallRectangle.X -  1;
                    WhiteBallRectangle.Y = WhiteBallRectangle.Y - 1;
                    //playerPos -= new Vector2(4, 4);
                }

                else if (keyboardState.IsKeyDown(Keys.Up) && keyboardState.IsKeyDown(Keys.Right))
                {
                    WhiteBallRectangle.Y = WhiteBallRectangle.Y - 1;
                    WhiteBallRectangle.X = WhiteBallRectangle.X + 1;
                    //playerPos -= new Vector2(0, 4);
                    //playerPos += new Vector2(4, 0);
                }
                else if (keyboardState.IsKeyDown(Keys.Down) && keyboardState.IsKeyDown(Keys.Left))
                {
                    WhiteBallRectangle.Y = WhiteBallRectangle.Y + 1;
                    WhiteBallRectangle.X = WhiteBallRectangle.X - 1;
                    //playerPos += new Vector2(0, 4);
                    //playerPos -= new Vector2(4, 0);
                }
                else if (keyboardState.IsKeyDown(Keys.Down) && keyboardState.IsKeyDown(Keys.Right))
                {
                    WhiteBallRectangle.Y = WhiteBallRectangle.Y + 1;
                    WhiteBallRectangle.X = WhiteBallRectangle.X + 1;
                }
                    //playerPos += new Vector2(4, 4);


        }


        CheckBounds();

        //Collision
       // Rectangle WhiteBallRectangle = new Rectangle((int)playerPos.X, (int)playerPos.Y, 10, 100);
        //Rectangle BlackBallRectangle = new Rectangle((int)playerPos.X, (int)playerPos.Y, 10, 100);
        if (WhiteBallRectangle.Intersects(BlueBallRectangle))
        {
            score = score + 20;
        }
        if (WhiteBallRectangle.Intersects(GreenBallRectangle))
        {

            score = score + 10;
        }
        if (WhiteBallRectangle.Intersects(OrangeBallRectangle))
        {

            score = score + 40;
        }
        if (WhiteBallRectangle.Intersects(PinkBallRectangle))
        {

            score = score + 25;
        }
        if (WhiteBallRectangle.Intersects(RedBallRectangle))
        {

            score = score + 10;
        }
        if (WhiteBallRectangle.Intersects(YellowBallRectangle))
        {

            score = score + 50;
        }
        if (WhiteBallRectangle.Intersects(BlackBallRectangle))
        {
            Exit();
        }
        if (WhiteBallRectangle.Intersects(BlackBallRectangle2))
        {
            Exit();
        }


        base.Update(gameTime);
    }

    private void CheckBounds()
    {
        if (WhiteBallRectangle.Y <= 0)
        {
            WhiteBallRectangle.Y = 1;
            canMove = false;
        }
        else if (WhiteBallRectangle.Y >= 452)
        {
            WhiteBallRectangle.Y = 451;
            canMove = false;
        }
        else
            canMove = true;

        if (WhiteBallRectangle.X <= 0)
        {
            WhiteBallRectangle.X = 1;
            canMove = false;
        }
        else if (playerPos.X >= 772)
        {
            WhiteBallRectangle.X = 771;
            canMove = false;
        }
        else
            canMove = true;
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        Vector2 text = new Vector2(10, 0);
        spriteBatch.Begin();


        spriteBatch.Draw(BackgroundTexture, position, Color.White);
        spriteBatch.DrawString(TitleFont, "Pick Up The Crew", text, Color.Black);
        spriteBatch.DrawString(TitleFont, "Score: " + score, scorePos, Color.Black);

       // spriteBatch.Draw(WhiteBallTexture, playerPos, null, Color.White, 0.0f, new Vector2(0, 0),
        //0.3f, SpriteEffects.None, 0.0f);
        //mainPlayer.Draw(spriteBatch);

        spriteBatch.Draw(WhiteBallTexture, WhiteBallRectangle, Color.White);
        spriteBatch.Draw(BlackBallTexture, BlackBallRectangle, Color.White);
        spriteBatch.Draw(BlackBallTexture2, BlackBallRectangle2, Color.White);
        spriteBatch.Draw(BlueBallTexture, BlueBallRectangle, Color.White);
        spriteBatch.Draw(GreenBallTexture, GreenBallRectangle, Color.White);
        spriteBatch.Draw(OrangeBallTexture, OrangeBallRectangle, Color.White);
        spriteBatch.Draw(PinkBallTexture, PinkBallRectangle, Color.White);
        spriteBatch.Draw(RedBallTexture, RedBallRectangle, Color.White);
        spriteBatch.Draw(YellowBallTexture, YellowBallRectangle, Color.White);

        /*spriteBatch.Draw(BlueBallTexture, new Vector2(500, (380 + (BlueBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(GreenBallTexture, new Vector2(230, (180 + (GreenBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(OrangeBallTexture, new Vector2(700, (200 + (OrangeBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(PinkBallTexture, new Vector2(600, (20 + (PinkBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(BlackBallTexture, new Vector2(100, (80 + (BlackBallTexture.Height / 2))), nul`enter code here`l, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(BlackBallTexture, new Vector2(300, (100 + (BlackBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
        spriteBatch.Draw(BlackBallTexture, new Vector2(400, (400 + (BlackBallTexture.Height / 2))), null, Color.White, 0.0f, new Vector2(0, 0),
        0.3f, SpriteEffects.None, 0.0f);
         */
        if (WhiteBallRectangle.Intersects(BlueBallRectangle))
        {
            spriteBatch.DrawString(TitleFont, "You Rescued Liuetenant Sky for 20 points!", saved, Color.Black);
        }
        spriteBatch.End();

        base.Draw(gameTime);
    }
}

}

Kieren Wells
  • 65
  • 1
  • 6
  • Welcome to SO. Your question sort of reads as a set of requirements. Though you show code there is no AI attempt. What research have you done? These things will help us help you. Good luck! _[How do I ask a good question?](http://stackoverflow.com/help/how-to-ask)_ –  May 01 '15 at 07:03

2 Answers2

0

Vector2.Distance(playerPos, BlackBallPos) will get you the distance between the objects, however you should probably use Vector2.DistanceSquared instead for performance.

To move toward the player, read this:

http://xnafan.net/2012/12/pointing-and-moving-towards-a-target-in-xna-2d/

Callum Bradbury
  • 936
  • 7
  • 14
  • _[Your answer is in another castle: when is an answer not an answer?](http://meta.stackexchange.com/questions/225370/your-answer-is-in-another-castle-when-is-an-answer-not-an-answer)_. Consider editing your answer to contain a summary of the article pointed to by the link. You are currently only describing _how to get the distance_ rather than _how do I move towards a target_ –  May 01 '15 at 10:47
  • I have considered and whilst I could paraphrase the article, it's not really fair to do so when the original author spent so much effort articulating how it works. – Callum Bradbury May 01 '15 at 12:36
  • 2
    _[Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.](http://stackoverflow.com/help/how-to-answer)_ –  May 01 '15 at 19:13
0

Not realy Q&A question, but hey...

First of all, start with reorganizating your game to components (ist realy big topic to discuss here)

Second, you can find distance with simple Pythagorean theorem, something like

var x = WhiteBallRectangle.X - BlackBallRectangle.X;
var y = WhiteBallRectangle.Y - BlackBallRectangle.Y;
var distance = (decimal)Math.Sqrt((Math.Pow(x, 2) + Math.Pow(y, 2));
if(distance < f)
{
  // do something
}

And what is that something? It depends what you want... Go full speed at your white? Then read about delta timing (for understanding acceleration in game etc). Direction is very simple Vector math:

var x = WhiteBallRectangle.X - BlackBallRectangle.X;
var y = WhiteBallRectangle.Y - BlackBallRectangle.Y;
var direction = new Vector2(x, y);
direction.Normalize();

note that x and y are same, so:

var x = WhiteBallRectangle.X - BlackBallRectangle.X;
var y = WhiteBallRectangle.Y - BlackBallRectangle.Y;
var distance = (decimal)Math.Sqrt((Math.Pow(x, 2) + Math.Pow(y, 2));
var direction = new Vector2(x, y); // from Black to White
direction.Normalize();
if(distance < f)
{
  // do something
}

I am pretty sure there are some fancy Vector.xxx methods which do lots of this for you, but understanding problem is always better ;)

As Micky Duncan pointed out in comment - i totally miss that "ai" part:

So, now about AI. AI is complex system (look for UCT / minimax), which is overpower for your small project. Becouse you did not described what you want, i will make up my own example:

if the distance is less then "far"
  go slowly
if the distance is less then "medium"
  go fast
if the distance is less then "near" OR greater then "far"
  stop

Distance and where to go we just computed, so its simple updating position (in your project just stick it in Update() method)

decimal far = 50.0m, medium = 30.0m, near = 10.0m; // tweak this
decimal slow = 10.0m, fast = 20.0m; // tweak this
var x = WhiteBallRectangle.X - BlackBallRectangle.X;
var y = WhiteBallRectangle.Y - BlackBallRectangle.Y;
var distance = (decimal)Math.Sqrt((x * x) + (y * y));
var direction = new Vector2(x, y); // from Black to White
direction.Normalize();
Vector2 move = new Vector2(0, 0);
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
if(distance < far)
{
   move = direction * slow * delta; // delta timing, explained at *
} else if (distance < medium && distance > near)
{
  move = direction * fast * delta; // delta timing, explained at *
}

BlackBallRectangle.X += move.X;
BlackBallRectangle.Y += move.Y;
  • delta timing:

    I used delta timing here, so your speed (slow and fast) are "per second", not "per frame" - except for physics (things like tunneling can occur at one PC and not at another) you really should be using this technique.

    I will give you one bonus example for delta timing:

    if (keyboardState.IsKeyDown(Keys.Up) && prevKeyboardState.IsKeyDown(Keys.Up)) WhiteBallRectangle.Y = WhiteBallRectangle.Y - (10 * delta);

    now it is 10 units (px?) per second, not "per-random-i-dont-really-know-how-fast-other-folk-pc-is-rendering-and-only-hope-it-is-60-frame-per-sec"

  • why normalized vector:

    We want only direction, without speed factor - we will multiple with speed in condition "if near" / "if far"

Its far from real AI, but its good enought for simple follow around on 2D without obstacles. You can smooth acceleration using (lets say) Lerp method and much more, this is really basic example.

Jan 'splite' K.
  • 1,667
  • 2
  • 27
  • 34
  • Thanks for this Splite, it really is helping me to understand what to do. When I implememnted the code that you supplied into my Update Method(), i altered the Black.Y to = BlackBallRectangle which removed the errors but I am still getting errors for 50.0, 30.0, 10.0, Vector and the if(distance – Kieren Wells May 03 '15 at 11:12
  • Oh, I wrote it from memory... `Vector` == `Vector2` and `50.0` / `30.0` / `10.0` == `50.0m` / `30.0m` / `10.0m` i will edit my answer... – Jan 'splite' K. May 03 '15 at 13:45
  • thanks, that would be great if you could post your edited answer :) – Kieren Wells May 03 '15 at 14:00
  • It's still coming up with a few errors, again the `var direction` and `if(distance < far` `else if (distance < medium && distance > near)` and I have no reason why? the error report is telling me that Operator < cannot be applied to operands of type double and decimal – Kieren Wells May 03 '15 at 14:11
  • Make some own research, i had to start VS to see what is wrong :D Math.Sqrt() is returning `Double`, we have `Decimal`, just add cast... `var distance = (decimal)Math.Sqrt((x * x) + (y * y));` or retype far/near/medium to `Double` (i edited my answer). You know, this is q&a site, its more like conceptual work and not `just give me code` – Jan 'splite' K. May 03 '15 at 14:22
  • I really do appreciate your help, its just that I am new to c# and xna – Kieren Wells May 03 '15 at 14:26
  • No harm done :) Dont forget to accept as answer & have some other question ;) – Jan 'splite' K. May 03 '15 at 14:58
  • I will do :) just one last question, what does the error message **Cannot assign void to an implicitly-typed local variable** mean? – Kieren Wells May 03 '15 at 15:01
  • something like `private void X(){ ... }`, `int x = X();` (but method X() is void - doesnt return anything, clr have no idea how to get `int` from void) – Jan 'splite' K. May 03 '15 at 15:08
  • ah okay! :D its just that that error is coming up in the code but I wont keep you any longer with my problems lol. Thanks again – Kieren Wells May 03 '15 at 15:12
  • `direction.Normalize();` fixed. – Jan 'splite' K. May 03 '15 at 15:22
  • That has fixed the cannot assign void except its made 2 new errors of **Operator '*' cannot be applied to operands** for `direction * slow` and at `move.X` its still saying **cannot implicitly convert type float to int – Kieren Wells May 03 '15 at 15:40
  • Hi again, I have my code working and have been trying to alter it but the BlackBall doesnt seem to be following the player, once i get close to it, they stop – Kieren Wells May 03 '15 at 21:00
  • please note `near` variable and what it do (maybe setting it to radius of your ball will help?) – Jan 'splite' K. May 03 '15 at 21:50
  • I tried that but it doesnt seemed to have worked, I may have written it wrong, not sure – Kieren Wells May 04 '15 at 13:19
  • I wrote something along the lines of `WhiteBallCenter.X = CurrentPosition.X + (xDistance / 2.0f); and WhiteBallCenter.Y = CurrentPosition.Y + (yDistance / 2.0f);` – Kieren Wells May 04 '15 at 14:33
  • what do I have to write? I have tried multiple things but none have worked – Kieren Wells May 04 '15 at 16:41
  • why are you adding `distance / 2` to position? Distance is only helping variable to decide when to move `fast` / `slow` / not at all – Jan 'splite' K. May 05 '15 at 10:32