我需要制作一个 AI 角色,当他们在 C# XNA 中达到一定距离时跟随玩家

I need to make an AI character that follows the player when they reach a certain distance in C# XNA

这是我目前的游戏代码。我想创建一个 AI 角色 (BlackBall),当玩家 (WhiteBall) 离开一定距离时,它会跟随玩家 (WhiteBall)。我不知道从哪里开始让它工作,但它将成为我游戏的主要部分,所以它是必不可少的。

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);
    }
}

}

Vector2.Distance(playerPos, BlackBallPos) 会让你得到物体之间的距离,但是你应该使用 Vector2.DistanceSquared 来提高性能。

要向玩家移动,请阅读:

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

不是真正的问答问题,但是嘿...

首先,从重组您的游戏开始 components(这里讨论的话题真的很大)

其次,你可以用简单的方法找到距离Pythagorean theorem, 像

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
}

那是什么东西?这取决于你想要什么......全速前进你的白色?然后阅读增量时间(用于了解游戏中的加速等)。方向很简单 矢量数学:

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

注意 x 和 y 相同,所以:

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
}

我很确定有一些奇特的 Vector.xxx 方法可以为您做很多这样的事情,但理解问题总是更好 ;)

正如 Micky Duncan 在评论中指出的那样——我完全怀念 "ai" 部分:

那么,现在谈谈 AI。 AI 是一个复杂的系统(寻找 UCT / minimax),这对你的小项目来说是压倒性的。因为你没有描述你想要的,我会自己编一个例子:

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

我们刚刚计算了距离和去哪里,所以它的简单更新位置(在您的项目中只需将其粘贴在 Update() 方法中)

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 计时,所以你的速度(slowfast)是 "per second",而不是 "per frame" - 除了物理(隧道效应可能会发生)在一台电脑上而不是在另一台电脑上)你真的应该使用这种技术。

    我会给你一个增量时间的额外例子:

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

    现在是每秒 10 个单位(px?),而不是 "per-random-i-dont-really-know-how-fast-other-folk-pc-is-rendering-and-only-hope-it-is-60-frame-per-sec"

  • 为什么归一化向量:

    我们只想要方向,没有速度因素 - 我们将在条件 "if near" / "if far"

  • 中乘以速度

它远非真正的 AI,但足以在 2D 上无障碍地进行简单的跟随。您可以使用(比方说)Lerp 方法等平滑加速,这是非常基本的示例。