1

一周前我是 XNA 的新手,但我在 C# 方面做了很多工作。我下载了 4.0 Platformer 入门套件。我浏览了教程,现在我试图让我的程序在我的角色向右移动时水平滚动背景(如超级马里奥兄弟)。

我让它工作了一些,但我的角色最终离开了屏幕,我的对象留在了框架中。用文字很难画出完美的画面,所以我录制了一段正在发生的事情的视频并放在这里

我不确定对齐在哪里。我包括我的 Player.cs 和 Layer2.cs。

请原谅我的背景音乐;我刚刚添加了我可以在网上找到的第一个 .wma 文件。

Player.cs
#region File Description
//-----------------------------------------------------------------------------
// Player.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace Platformer
{
/// <summary>
/// Our fearless adventurer!
/// </summary>
class Player
{
    // Animations
    private Animation idleAnimation;
    private Animation idleAnimationBack;
    private Animation runAnimation;
    private Animation jumpAnimation;
    private Animation celebrateAnimation;
    private Animation dieAnimation;
    private SpriteEffects flip = SpriteEffects.None;
    private AnimationPlayer sprite;

    // Sounds
    private SoundEffect killedSound;
    private SoundEffect jumpSound;
    private SoundEffect fallSound;

    public Level Level
    {
        get { return level; }
    }
    Level level;

    public bool IsAlive
    {
        get { return isAlive; }
    }
    bool isAlive;

    // Physics state
    public Vector2 Position
    {
        get { return position; }
        set { position = value; }
    }
    Vector2 position;

    private float previousBottom;

    public Vector2 Velocity
    {
        get { return velocity; }
        set { velocity = value; }
    }
    Vector2 velocity;

    // Constants for controling horizontal movement
    private const float MoveAcceleration = 13000.0f;
    private const float MaxMoveSpeed = 1750.0f;
    private const float GroundDragFactor = 0.48f;
    private const float AirDragFactor = 0.58f;

    // Constants for controlling vertical movement
    private const float MaxJumpTime = 0.30f;
    private const float JumpLaunchVelocity = -3500.0f;
    private const float GravityAcceleration = 3400.0f;
    private const float MaxFallSpeed = 550.0f;
    private const float JumpControlPower = 0.14f; 

    // Input configuration
    private const float MoveStickScale = 1.0f;
    private const float AccelerometerScale = 1.5f;
    private const Buttons JumpButton = Buttons.A;
    private const Buttons BackwardButton = Buttons.LeftStick;

    /// <summary>
    /// Gets whether or not the player's feet are on the ground.
    /// </summary>
    public bool IsOnGround
    {
        get { return isOnGround; }
    }
    bool isOnGround;

    /// <summary>
    /// Current user movement input.
    /// </summary>
    private float movement;

    // Jumping state
    private bool isJumping;
    private bool wasJumping;
    private float jumpTime;
    private bool isBackwards;

    private Rectangle localBounds;
    /// <summary>
    /// Gets a rectangle which bounds this player in world space.
    /// </summary>
    public Rectangle BoundingRectangle
    {
        get
        {
            int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
            int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;

            return new Rectangle(left, top, localBounds.Width, localBounds.Height);
        }
    }

    /// <summary>
    /// Constructors a new player.
    /// </summary>
    public Player(Level level, Vector2 position)
    {
        this.level = level;

        LoadContent();

        Reset(position);
    }

    /// <summary>
    /// Loads the player sprite sheet and sounds.
    /// </summary>
    public void LoadContent()
    {
        // Load animated textures.
        idleAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewIdle"), 0.1f, true);
        idleAnimationBack = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewIdleBack"), 0.1f, true);
        runAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewRun5"), 0.1f, true);
        jumpAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewJump2"), 0.1f, false);
        celebrateAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewJump"), 0.1f, false);
        dieAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewDie"), 0.1f, false);

        // Calculate bounds within texture size.            
        int width = (int)(idleAnimation.FrameWidth * 0.4);
        int left = (idleAnimation.FrameWidth - width) / 2;
        int height = (int)(idleAnimation.FrameWidth * 0.9);
        int top = idleAnimation.FrameHeight - height;
        localBounds = new Rectangle(left, top, width, height);

        // Load sounds.            
        killedSound = Level.Content.Load<SoundEffect>("Sounds/PlayerKilled");
        jumpSound = Level.Content.Load<SoundEffect>("Sounds/PlayerJump");
        fallSound = Level.Content.Load<SoundEffect>("Sounds/PlayerFall");
    }

    /// <summary>
    /// Resets the player to life.
    /// </summary>
    /// <param name="position">The position to come to life at.</param>
    public void Reset(Vector2 position)
    {
        Position = position;
        Velocity = Vector2.Zero;
        isAlive = true;
        sprite.PlayAnimation(idleAnimation);
    }

    /// <summary>
    /// Handles input, performs physics, and animates the player sprite.
    /// </summary>
    /// <remarks>
    /// We pass in all of the input states so that our game is only polling the hardware
    /// once per frame. We also pass the game's orientation because when using the accelerometer,
    /// we need to reverse our motion when the orientation is in the LandscapeRight orientation.
    /// </remarks>
    public void Update(
        GameTime gameTime, 
        KeyboardState keyboardState, 
        GamePadState gamePadState, 
        TouchCollection touchState, 
        AccelerometerState accelState,
        DisplayOrientation orientation)
    {
        GetInput(keyboardState, gamePadState, touchState, accelState, orientation);

        ApplyPhysics(gameTime);

        if (IsAlive && IsOnGround)
        {
            if (Math.Abs(Velocity.X) - 0.02f > 0)
            {
                sprite.PlayAnimation(runAnimation);
            }
            else
            {
                sprite.PlayAnimation(idleAnimation);
            }
        }

        // Clear input.
        movement = 0.0f;
        isJumping = false;
    }

    /// <summary>
    /// Gets player horizontal movement and jump commands from input.
    /// </summary>
    private void GetInput(
        KeyboardState keyboardState, 
        GamePadState gamePadState, 
        TouchCollection touchState,
        AccelerometerState accelState, 
        DisplayOrientation orientation)
    {
        // Get analog horizontal movement.
        movement = gamePadState.ThumbSticks.Left.X * MoveStickScale;

        // Ignore small movements to prevent running in place.
        if (Math.Abs(movement) < 0.5f)
            movement = 0.0f;

        // Move the player with accelerometer
        if (Math.Abs(accelState.Acceleration.Y) > 0.10f)
        {
            // set our movement speed
            movement = MathHelper.Clamp(-accelState.Acceleration.Y * AccelerometerScale, -1f, 1f);

            // if we're in the LandscapeLeft orientation, we must reverse our movement
            if (orientation == DisplayOrientation.LandscapeRight)
                movement = -movement;
        }

        // If any digital horizontal movement input is found, override the analog movement.
        if (gamePadState.IsButtonDown(Buttons.DPadLeft) ||
            keyboardState.IsKeyDown(Keys.Left) ||
            keyboardState.IsKeyDown(Keys.A))
        {
            movement = -1.0f;
        }
        else if (gamePadState.IsButtonDown(Buttons.DPadRight) ||
                 keyboardState.IsKeyDown(Keys.Right) ||
                 keyboardState.IsKeyDown(Keys.D))
        {
            movement = 1.0f;
        }

        // Check if the player wants to jump.
        isJumping =
            gamePadState.IsButtonDown(JumpButton) ||
            keyboardState.IsKeyDown(Keys.Space) ||
            keyboardState.IsKeyDown(Keys.W) ||
            touchState.AnyTouch();

        // Check if the player is pushing up.
        isBackwards =
        gamePadState.IsButtonDown(Buttons.DPadUp) ||
        gamePadState.ThumbSticks.Left.Y > 0 ||
        keyboardState.IsKeyDown(Keys.Up);





    }

    /// <summary>
    /// Updates the player's velocity and position based on input, gravity, etc.
    /// </summary>
    public void ApplyPhysics(GameTime gameTime)
    {
        float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

        Vector2 previousPosition = Position;

        // Base velocity is a combination of horizontal movement control and
        // acceleration downward due to gravity.
        velocity.X += movement * MoveAcceleration * elapsed;
        velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);

        velocity.Y = DoJump(velocity.Y, gameTime);

        // Apply pseudo-drag horizontally.
        if (IsOnGround)
            velocity.X *= GroundDragFactor;
        else
            velocity.X *= AirDragFactor;

        // Prevent the player from running faster than his top speed.            
        velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);

        // Apply velocity.
        Position += velocity * elapsed;
        Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));

        // If the player is now colliding with the level, separate them.
        HandleCollisions();

        // If the collision stopped us from moving, reset the velocity to zero.
        if (Position.X == previousPosition.X)
            velocity.X = 0;

        if (Position.Y == previousPosition.Y)
            velocity.Y = 0;

        DoBackwards(gameTime);
    }

    /// <summary>
    /// Calculates the Y velocity accounting for jumping and
    /// animates accordingly.
    /// </summary>
    /// <remarks>
    /// During the accent of a jump, the Y velocity is completely
    /// overridden by a power curve. During the decent, gravity takes
    /// over. The jump velocity is controlled by the jumpTime field
    /// which measures time into the accent of the current jump.
    /// </remarks>
    /// <param name="velocityY">
    /// The player's current velocity along the Y axis.
    /// </param>
    /// <returns>
    /// A new Y velocity if beginning or continuing a jump.
    /// Otherwise, the existing Y velocity.
    /// </returns>
    private float DoJump(float velocityY, GameTime gameTime)
    {
        // If the player wants to jump
        if (isJumping)
        {
            // Begin or continue a jump
            if ((!wasJumping && IsOnGround) || jumpTime > 0.0f)
            {
                if (jumpTime == 0.0f)
                    jumpSound.Play();

                jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
                sprite.PlayAnimation(jumpAnimation);
            }

            // If we are in the ascent of the jump
            if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
            {
                // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
                velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
            }
            else
            {
                // Reached the apex of the jump
                jumpTime = 0.0f;
            }
        }
        else
        {
            // Continues not jumping or cancels a jump in progress
            jumpTime = 0.0f;
        }
        wasJumping = isJumping;

        return velocityY;
    }

    private void DoBackwards(GameTime gameTime)
    {
        Animation idleOrig = idleAnimation;

        // If the player pushes up
        if (isBackwards)
        {
            idleAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewIdleBack"), 0.2f, true);
        }
        else
        {
            idleAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/DrewIdle"), 0.1f, true);
        }
    }

    /// <summary>
    /// Detects and resolves all collisions between the player and his neighboring
    /// tiles. When a collision is detected, the player is pushed away along one
    /// axis to prevent overlapping. There is some special logic for the Y axis to
    /// handle platforms which behave differently depending on direction of movement.
    /// </summary>
    private void HandleCollisions()
    {
        // Get the player's bounding rectangle and find neighboring tiles.
        Rectangle bounds = BoundingRectangle;
        int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
        int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
        int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
        int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

        // Reset flag to search for ground collision.
        isOnGround = false;

        // For each potentially colliding tile,
        for (int y = topTile; y <= bottomTile; ++y)
        {
            for (int x = leftTile; x <= rightTile; ++x)
            {
                // If this tile is collidable,
                TileCollision collision = Level.GetCollision(x, y);
                if (collision != TileCollision.Passable)
                {
                    // Determine collision depth (with direction) and magnitude.
                    Rectangle tileBounds = Level.GetBounds(x, y);
                    Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                    if (depth != Vector2.Zero)
                    {
                        float absDepthX = Math.Abs(depth.X);
                        float absDepthY = Math.Abs(depth.Y);

                        // Resolve the collision along the shallow axis.
                        if (absDepthY < absDepthX || collision == TileCollision.Platform)
                        {
                            // If we crossed the top of a tile, we are on the ground.
                            if (previousBottom <= tileBounds.Top)
                                isOnGround = true;

                            // Ignore platforms, unless we are on the ground.
                            if (collision == TileCollision.Impassable || IsOnGround)
                            {
                                // Resolve the collision along the Y axis.
                                Position = new Vector2(Position.X, Position.Y + depth.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                        else if (collision == TileCollision.Impassable) // Ignore platforms.
                        {
                            // Resolve the collision along the X axis.
                            Position = new Vector2(Position.X + depth.X, Position.Y);

                            // Perform further collisions with the new bounds.
                            bounds = BoundingRectangle;
                        }
                    }
                }
            }
        }

        // Save the new bounds bottom.
        previousBottom = bounds.Bottom;
    }

    /// <summary>
    /// Called when the player has been killed.
    /// </summary>
    /// <param name="killedBy">
    /// The enemy who killed the player. This parameter is null if the player was
    /// not killed by an enemy (fell into a hole).
    /// </param>
    public void OnKilled(Enemy killedBy)
    {
        isAlive = false;

        if (killedBy != null)
            killedSound.Play();
        else
            fallSound.Play();

        sprite.PlayAnimation(dieAnimation);
    }

    /// <summary>
    /// Called when this player reaches the level's exit.
    /// </summary>
    public void OnReachedExit()
    {
        sprite.PlayAnimation(celebrateAnimation);
    }

    /// <summary>
    /// Draws the animated player.
    /// </summary>
    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        // Flip the sprite to face the way we are moving.
        if (Velocity.X > 0)
            flip = SpriteEffects.FlipHorizontally;
        else if (Velocity.X < 0)
            flip = SpriteEffects.None;

        // Draw that sprite.
        sprite.Draw(gameTime, spriteBatch, Position, flip);
    }
}
}

Layer2.cs
#region File Description
//-----------------------------------------------------------------------------
// Layer2.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Platformer
{
class Layer
{
    public Texture2D[] Textures { get; private set; }
    public float ScrollRate { get; private set; }

    public Layer(ContentManager content, string basePath, float scrollRate)
    {
        // Assumes each layer only has 3 segments.
        Textures = new Texture2D[3];
        for (int i = 0; i < 3; ++i)
            Textures[i] = content.Load<Texture2D>(basePath + "_" + i);

        ScrollRate = scrollRate;
    }

    public void Draw(SpriteBatch spriteBatch, float cameraPosition)
    {
        // Assume each segment is the same width.
        int segmentWidth = Textures[0].Width;

        // Calculate which segments to draw and how much to offset them.
        float x = cameraPosition * ScrollRate;
        int leftSegment = (int)Math.Floor(x / segmentWidth);
        int rightSegment = leftSegment + 1;
        x = (x / segmentWidth - leftSegment) * -segmentWidth;

        spriteBatch.Draw(Textures[leftSegment % Textures.Length], new Vector2(x, 0.0f), Color.White);
        spriteBatch.Draw(Textures[rightSegment % Textures.Length], new Vector2(x + segmentWidth, 0.0f), Color.White);
    }
}
}
4

1 回答 1

0

最好从样品套件开始。但是套件并没有应有的那么好。因此,我强烈建议不要使用该套件,而是使用其他示例来开始。

在这里,我列出了其中的一些,包括横向滚动游戏和 2D 游戏。

Rimer 的 2D 教程是我发现的最好的 2D XNA 游戏入门教程之一。即使它有3D系列。

【Nick Gravelyn 的瓷砖引擎教程系列】(http://www.youtube.com/playlist?list=PL0A865073DA96A7DA)精彩视频教程系列包括2D地图编辑器、动画、边界绑定、碰撞。创建精彩的 2D 游戏所需的几乎所有知识。横向滚动,RPG风格。

最后但并非最不重要的三个必须有书 学习 XNA XNA 游戏编程食谱 构建游戏独立开发人员的实用指南

我希望我的回答可以帮助您学习 XNA。

于 2013-08-09T07:27:40.603 回答