バネオプション


バネとかゴムひものような動きをするオプションを作ってみましょう。
普通に引っ張って移動するだけだとイマイチ面白くなかったので、位置ロック機能を付けました。ボタンを押している間は位置が固定され、離すと解除されます。固定して引っ張ってから解除すると、さながら、ゴムゴムのピストル!という感じになります。



考え方

バネ的な動きを作るのは、それほど難しくはないです。
慣性のあるオプション+アルファって感じです。

バネは、長く引き伸ばすほど、強い力で縮もうとします。つまり、遠くにあるほど、強い力で元の場所に戻ろうとします。そういうプログラムを書いてあげればOKです。

あとは、摩擦力というか空気抵抗というか、そういう速度を減衰させる処理が必要です。さもないと、永久にビヨンビヨンし続けてしまいます。




サンプルコード

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Spring
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        // プレイヤー移動速度
        const float PlayerSpeed = 7.0f;
        // オプションの距離
        const float OptionDistance = 80f;
        // オプションのバネ係数
        const float Spring = 0.2f;
        // オプションの減衰率
        const float Dump = 0.947f;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // ぱっちぃのテクスチャ
        Texture2D texturePacchi;
        // かまトゥのテクスチャ
        Texture2D textureKamatoo;
        // プレイヤーの位置
        Vector2 playerPosition = new Vector2(200, 200);
        // オプションの位置
        Vector2 optionPosition;
        // オプションの速度
        Vector2 optionVelocity;

        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()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            // 画像を読み込む
            texturePacchi = Content.Load<Texture2D>("pacchi_32");
            textureKamatoo = Content.Load<Texture2D>("kamatoo_32");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// game-specific 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)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here

            // 上下左右が押されたらプレイヤーを移動させる
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
            {
                playerPosition.X -= PlayerSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
            {
                playerPosition.X += PlayerSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
            {
                playerPosition.Y -= PlayerSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                playerPosition.Y += PlayerSpeed;
            }

            // Spaceキーを押している間は、オプションをその場に固定する
            if (Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                optionVelocity = Vector2.Zero;
            }
            // Spaceキーを押してないときは、あるべき位置に向かってバネの力で移動する
            else
            {
                // オプションがあるべき位置を算出
                Vector2 desiredPosition = CalcOptionPosition(optionPosition, playerPosition);

                // オプションの現在の位置とあるべき位置の差分
                Vector2 delta = desiredPosition - optionPosition;

                // 差分が大きいほど(あるべき位置から離れているほど)強い力で、あるべき位置に向かうようにする
                optionVelocity += delta * Spring;

                // 速度を減衰させる(摩擦抵抗とか空気抵抗みたいなもの)
                optionVelocity *= Dump;

                // 速度の分だけ移動させる
                optionPosition += optionVelocity;
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// オプションのあるべき位置を計算する
        /// </summary>
        /// <param name="currentPosition">オプションの現在の位置</param>
        /// <param name="playerPosition">自機の位置</param>
        /// <returns>あるべき位置</returns>
        Vector2 CalcOptionPosition(Vector2 currentPosition, Vector2 playerPosition)
        {
            // オプションと親の距離を算出
            float distance = Vector2.Distance(playerPosition, currentPosition);

            // 距離が一定以下の場合は、移動の必要はない
            if (distance <= OptionDistance)
                return currentPosition;

            // 親から見たオプションへの方向ベクトルを算出
            Vector2 direction = currentPosition - playerPosition;

            // 正規化(長さを1に)
            direction.Normalize();

            // 親から上限まで離した位置があるべき場所
            return playerPosition + direction * OptionDistance;
        }

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

            // TODO: Add your drawing code here

            spriteBatch.Begin();

            // オプションを描画
            spriteBatch.Draw(textureKamatoo, optionPosition, Color.White);

            // プレイヤーを描画
            spriteBatch.Draw(texturePacchi, playerPosition, Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}


定数Springがバネの強さで、定数Dumpが空気抵抗みたいなやつです。
値をいろいろと変更して遊んでみてください。モッッサリしたり、ビヨビヨビヨビヨン!ってなったり、いろいろ変わります。



色んなオプションを作る目次へ戻る




0 件のコメント:

コメントを投稿