Сегодня начнем знакомиться с 2d физическим движком, который можно использовать в XNA. http://farseerphysics.codeplex.com . Движок бесплатный, разработка новых версий ведется. Использую версию 2.1.3 (c# 2008 express, XNA 3.1)
Для начала я приведу самый простой код чтобы быстро понять базис и сделать «привет миру».
Создаем проект из шаблона winGame, подключаем библиотеку, которую нужно скачать на сайте разработчика (взять готовую или взять проект и скомпилировать у себя).
Подключил в общие ссылки, потому что хотела XNA 3.0, а у меня 3.1. Но это не страшно, потому что мы только знакомимся, - ошибку можно исправить методами студии и с выходом новой версии для полноценного релиза игры.
Задумка такая: есть 2 объекта один из них статичен, а второй динамичный и управляется нами: Коробка, на столе. Управляем коробкой, а не столом :)
Первое что нам нужно это добавить пространства имен в проект:
В конструкторе класса создаем физический симулятор, и задаем ему направление гравитации:
Для начала я приведу самый простой код чтобы быстро понять базис и сделать «привет миру».
Создаем проект из шаблона winGame, подключаем библиотеку, которую нужно скачать на сайте разработчика (взять готовую или взять проект и скомпилировать у себя).
Подключил в общие ссылки, потому что хотела XNA 3.0, а у меня 3.1. Но это не страшно, потому что мы только знакомимся, - ошибку можно исправить методами студии и с выходом новой версии для полноценного релиза игры.
Задумка такая: есть 2 объекта один из них статичен, а второй динамичный и управляется нами: Коробка, на столе. Управляем коробкой, а не столом :)
Первое что нам нужно это добавить пространства имен в проект:
using FarseerGames.FarseerPhysics; using FarseerGames.FarseerPhysics.Interfaces; using FarseerGames.FarseerPhysics.Dynamics; using FarseerGames.FarseerPhysics.Collisions; using FarseerGames.FarseerPhysics.Factories;В свойства основного класса добавляем:
PhysicsSimulator physicsSimulator; Body boxBody; Geom boxGeom; float boxMass=0.1f; float boxWidth = 100; float boxHeight = 100; Texture2D boxTexture; Body platformBody; Geom platformGeom; float platformWidth = 1000; float platformHeight = 100; Texture2D platformTexture;Опишу то что ново PhysicsSimulator это главный класс самого симулятора – без него никак именно сюда все складывается обрабатывается и обновляется. Каждый объект движка должен иметь два класса: свойства самого тела (Body) – вес, инерция, направление силы и т.д.; и его геометрию Geom (координатные характеристики) – для расчета коллизий и взаимодействий с другими объектами.
В конструкторе класса создаем физический симулятор, и задаем ему направление гравитации:
physicsSimulator = new PhysicsSimulator(new Vector2(0, 100));А в методе обновления добавим обновление симулятора
physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds * 0.001f);Теперь перейдем к методу загрузки данных (контента):
boxBody = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, boxWidth, boxHeight, boxMass); boxBody.Position = new Vector2(700, 100); boxGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, boxBody, boxWidth, boxHeight); boxTexture = new Texture2D(GraphicsDevice, (int)boxWidth, (int)boxHeight, 1, TextureUsage.None, SurfaceFormat.Color); Color[] color = new Color[(int)boxWidth * (int)boxHeight]; for (int i = 0; i < color.Length; i++) color[i] = Color.Red; boxTexture.SetData(color); platformBody = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, platformWidth, platformHeight, 1); platformGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, platformBody, platformWidth, platformHeight); platformBody.Position = new Vector2(1280/2,720/2); platformBody.IsStatic = true; platformTexture = new Texture2D(GraphicsDevice, (int)platformWidth, (int)platformHeight, 1, TextureUsage.None, SurfaceFormat.Color); Color[] bcolor = new Color[(int)platformWidth * (int)platformHeight]; for (int i = 0; i < bcolor.Length; i++) bcolor[i] = Color.Green; platformTexture.SetData(bcolor);С помощью BodyFactory создаем тело с параметрами ширина, высота, масса. Задаем ему начальную позицию. Далее создаем этому телу геометрию, опять же с помощью фабрики, но уже GeomFactory, передаем тело, ширину, высоту. То же самое производим с телом платформой (столом) только после всех действий указываем, что оно статично: platformBody.IsStatic = true. Текстуры я не готовил специально – потому, что так код становиться более универсальный, и самое главное, что так виднее узреть само взаимодействие. Для создания монотонной текстуры средствами кода я использовал:
boxTexture = new Texture2D(GraphicsDevice, (int)boxWidth, (int)boxHeight, 1, TextureUsage.None, SurfaceFormat.Color); Color[] color = new Color[(int)boxWidth * (int)boxHeight]; for (int i = 0; i < color.Length; i++) color[i] = Color.Red; boxTexture.SetData(color);Тут нечего описывать: создается пустая текстура и попиксельно собираться цвет для ее закрашивания. Чтобы управлять объектом - добавляем код в метод обновления:
if (keyboardState.IsKeyDown(Keys.Up)) boxBody.ApplyForce(new Vector2(0, -100)); if (keyboardState.IsKeyDown(Keys.Down)) boxBody.ApplyForce(new Vector2(0, 100)); if (keyboardState.IsKeyDown(Keys.Left)) boxBody.ApplyForce(new Vector2(-100, 0)); if (keyboardState.IsKeyDown(Keys.Right)) boxBody.ApplyForce(new Vector2(100, 0));В соответствии одной из четырех клавиш (вверх, вниз, влево, вправо) применяем вектор силы методом ApplyForce. Часто нужно остановить тело внезапно, я не нашел встроенного метода чтобы реализовать это нужно использовать такую формулу (по нажатию на пробел тело останавливается, но не перестает крутиться):
if (keyboardState.IsKeyDown(Keys.Space)) boxBody.ApplyImpulse(boxBody.LinearVelocity * -boxBody.Mass);Полный текст кода
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; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; using FarseerGames.FarseerPhysics; using FarseerGames.FarseerPhysics.Interfaces; using FarseerGames.FarseerPhysics.Dynamics; using FarseerGames.FarseerPhysics.Collisions; using FarseerGames.FarseerPhysics.Factories; namespace FarseerEx1 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; PhysicsSimulator physicsSimulator; Body boxBody; Geom boxGeom; float boxMass=0.1f; float boxWidth = 100; float boxHeight = 100; Texture2D boxTexture; Body platformBody; Geom platformGeom; float platformWidth = 1000; float platformHeight = 100; Texture2D platformTexture; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferWidth = 1280; graphics.PreferredBackBufferHeight = 720; physicsSimulator = new PhysicsSimulator(new Vector2(0, 100)); } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); boxBody = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, boxWidth, boxHeight, boxMass); boxBody.Position = new Vector2(700, 100); boxGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, boxBody, boxWidth, boxHeight); boxTexture = new Texture2D(GraphicsDevice, (int)boxWidth, (int)boxHeight, 1, TextureUsage.None, SurfaceFormat.Color); Color[] color = new Color[(int)boxWidth * (int)boxHeight]; for (int i = 0; i < color.Length; i++) color[i] = Color.Red; boxTexture.SetData(color); platformBody = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, platformWidth, platformHeight, 1); platformGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, platformBody, platformWidth, platformHeight); platformBody.Position = new Vector2(1280/2,720/2); platformBody.IsStatic = true; platformTexture = new Texture2D(GraphicsDevice, (int)platformWidth, (int)platformHeight, 1, TextureUsage.None, SurfaceFormat.Color); Color[] bcolor = new Color[(int)platformWidth * (int)platformHeight]; for (int i = 0; i < bcolor.Length; i++) bcolor[i] = Color.Green; platformTexture.SetData(bcolor); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { base.Update(gameTime); KeyboardState keyboardState=Keyboard.GetState(); if (keyboardState.IsKeyDown(Keys.Up)) boxBody.ApplyForce(new Vector2(0, -100)); if (keyboardState.IsKeyDown(Keys.Down)) boxBody.ApplyForce(new Vector2(0, 100)); if (keyboardState.IsKeyDown(Keys.Left)) boxBody.ApplyForce(new Vector2(-100, 0)); if (keyboardState.IsKeyDown(Keys.Right)) boxBody.ApplyForce(new Vector2(100, 0)); if (keyboardState.IsKeyDown(Keys.Space)) boxBody.ApplyImpulse(boxBody.LinearVelocity * -boxBody.Mass); physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds * 0.001f); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); spriteBatch.Draw(boxTexture, boxBody.Position, null, Color.White, boxBody.Rotation, new Vector2(boxTexture.Width / 2, boxTexture.Height / 2), 1, SpriteEffects.None, 0); spriteBatch.Draw(platformTexture, platformBody.Position, null, Color.White, platformBody.Rotation, new Vector2(platformTexture.Width / 2, platformTexture.Height / 2), 1, SpriteEffects.None, 0); spriteBatch.End(); base.Draw(gameTime); } } }Файл проект FarseerEx1.zip
Комментарии
Отправить комментарий