【问题标题】:I need to make an AI character that follows the player when they reach a certain distance in C# XNA当玩家在 C# XNA 中达到一定距离时,我需要制作一个跟随玩家的 AI 角色
【发布时间】:2015-04-30 13:49:27
【问题描述】:

这是我目前的游戏代码。我想创建一个 AI 角色(BlackBall),它会在玩家(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);
    }
}

}

【问题讨论】:

  • 欢迎来到 SO。您的问题有点像一组要求。尽管您显示代码,但没有 AI 尝试。你做了什么研究?这些东西将帮助我们帮助您。祝你好运! How do I ask a good question?

标签: c# xna artificial-intelligence path-finding


【解决方案1】:

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

首先,将你的游戏重新组织为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 是复杂的系统(寻找 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;
  • 增量时间:

    我在这里使用了增量计时,所以你的速度(slowfast)是“每秒”,而不是“每帧”——物理除外(像隧道这样的事情可以在一台 PC 上发生,而不是在另一台 PC 上)你真的应该使用这种技术。

    我会给你一个关于增量计时的额外示例:

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

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

  • 为什么要归一化向量:

    我们只想要方向,没有速度因素 - 我们将在“如果接近”/“如果远”的条件下倍增速度

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

【讨论】:

  • 感谢这个 Splite,它确实帮助我理解了该做什么。当我实施您提供给我的 Update Method() 的代码时,我将 Black.Y 更改为 = BlackBallRectangle,这消除了错误,但我仍然收到 50.0、30.0、10.0、Vector 和 if(distance
  • 哦,我凭记忆写的...Vector == Vector250.0 / 30.0 / 30.0 / 10.0 == 50.0m / 30.0m / @987654337 @我会编辑我的答案...
  • 谢谢,如果您能发布您编辑的答案,那就太好了:)
  • 它仍然会出现一些错误,再次是var directionif(distance &lt; far else if (distance &lt; medium &amp;&amp; distance &gt; near),我没有理由吗?错误报告告诉我 Operator
  • 进行一些自己的研究,我必须启动 VS 看看有什么问题:D Math.Sqrt() 正在返回 Double,我们有 Decimal,只需添加演员表...@987654343 @ 或将 far/near/medium 重新输入到 Double(我编辑了我的答案)。你知道,这是问答网站,它更像是概念性工作,而不是just give me code
【解决方案2】:

Vector2.Distance(playerPos, BlackBallPos) 将为您提供对象之间的距离,但是您可能应该使用 Vector2.DistanceSquared 来代替性能。

要向玩家移动,请阅读以下内容:

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

【讨论】:

猜你喜欢
  • 2021-04-05
  • 1970-01-01
  • 2022-11-20
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多