Введение в Физику мягких форм (Перевод) Часть 3

  1. Классы Интегратора

Вспомните, что, как только у нас есть ускорение объекта, мы должны выполнить интеграцию относительно времени, чтобы получить скорость и позицию объекта. В реальном времени мы не в состоянии выполнить интеграцию аналитическим способом, подразумевая, что Вы не можете просто интегрировать уравнение как то, что Вы делали в своих основных вычислительных классах (например, «добавьте что-то к экспоненте и разделите целое уравнение на нее …»). Что нам может помочь — это численный метод, названный числовой интеграцией. Такое числовое решение позволяет нам производить выборку в отдельных точках интеграла, чтобы получить приближенное решение. В нашем случае мы производим выборку интеграла, каждый временной интервал (для пошаговых интеграторов). Числовые интеграторы позволяют нам использовать некоторые уравнения, вовлекающие ускорение, скорость и позицию, чтобы узнать приблизительную новую позицию объекта.


Одна главная идея, которая связана с числовыми интеграторами, — то, что всегда есть ошибка, связанная с интегратором, который мы используем. Это грубость вычисления зависит от порядка интегратора. Чем выше порядок, тем меньше ошибка. Однако более высокие порядки метода интеграции обычно означают большее количество вычислений и таким образом замедление в выполнении.

Есть множество интеграторов из которых мы можем выбирать, в пределах от эйлерова метода интеграции, который является самым быстрым, но не очень устойчивым (первый порядок), до Четвертого порядка Рунге-Кутта, который очень устойчив, но очень дорог в вычислениях. Для игр есть популярная методика средних групп по имени интеграция Verlet, которая быстра и устойчива (второй порядок). Для этой обучающей программы мы начнем с эйлерова метода интеграции, так как он является самым простым и осуществимым, и затем перейти к интегратору Verlet в более поздних главах.

Эти интеграторы требуют различных атрибутов объекта. Некоторые требуют, чтобы Вы сохранили текущую и предыдущую позицию, в то время как некоторые требуют, чтобы Вы сохранили настоящее положение и текущую скорость. Но новая позиция для следующего временного шага всегда сгенерируется, и она будет использована, чтобы обновить свойства CurrPosition из SimObject.

Теперь, после краткого обзора числовых интеграторов, мы можем начать создавать классы Интегратора. Эти классы в основном содержат ускорении SimObject, и выполняет числовую интеграцию, чтобы найти новую позицию и возможно скорость.

1.4.1 Абстрактный класс Интегратора

Как обычно, мы сначала создадим абстрактный класс, для того чтобы иметь возможность из него получить остальные классы интегратора. Во-первых, создайте новую папку Integrators под папкой SoftBody(under the SoftBody folder), и создайте новый файл класса по имени Integator.cs в ней.

(Integrator.cs)
using Microsoft.Xna.Framework;

using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.Integrators

{

    public
abstract
class
Integrator

    {

        private
Game game;

        //use a fixed time step to get predictable sim

        protected
float fixedTimeStep;

        public
float FixedTimeStep

        {

            get { return fixedTimeStep; }

            set { fixedTimeStep = value; }

        }
        //————————————————————-
        public Integrator(Game game)

        {

            this.game = game;
            //set the fixed time step to target elapsed time (default at 1/60)

            fixedTimeStep = (float)game.TargetElapsedTime.TotalSeconds;

        }

    }

}

Абстрактный класс Integrator определяет переменную класса fixedTimeStep, которая сохраняет установленное значение временного шага для моделирования. Поведение моделирования изменяется, как только изменяется временной шаг, это значит, что сила упругости, например, с той же самой жесткостью и константами сопротивления будет вести себя по-разному, подвергаясь моделированию в 30 кадров в секунду и в 60 кадров в секунду. Чтобы получить непротиворечивые и предсказуемые моделирования, лучше сделать это постоянным значением. Есть другие решения, такие как адаптивные методы временного шага, но мы будем придерживаться простого решения в нашей обучающей программе. Для fixedTimeStep переменной мы будем использовать значение из game.TargetElapsedTime, которое определяет желательный временной шаг для игры. Значение по умолчанию для этого 1 / 60 секунд.

Производные классы должны будут осуществить свой собственный метод Integrate, таким образом, он определен как абстрактный класс.

(абстрактный метод класса в Integrator.cs)
        public
abstract
void Integrate(Vector3 acceleration, SimObject simObject);

1.4.2 Прямой метод Эйлера

Мы можем теперь начать обсуждать наш первый числовой интегратор — Прямой метод Эйлера. Это — один из самых основных методов интегрирования.

Чтобы найти значение атрибута y в следующем временном шаге, Прямой метод Эйлера в основном находит результат произведения временного шага ∆t и производной y, и добавляет результат к y на текущем шаге:

y(t + ∆t) = y(t) + y'(t)∆t [5]

Связывая это с нашим моделированием, мы получим:

v(t + ∆t) = v(t) + a(t) ∆t [6]

Это означает, что скорость в следующем временном шаге, равняется скорости в текущее время, добавленной к результату произведения временного шага и ускорения в текущее время.

Подобное уравнение существует для позиции объекта:

x(t + ∆t) = x(t) + v(t) ∆t [7]

Теперь создайте новый файл и назовите его ForwardEulerIntegrator.cs.

(ForwardEulerIntegrator.cs)
using Microsoft.Xna.Framework;

using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.Integrators

{

    public
sealed
class
ForwardEulerIntegrator : Integrator

    {

        public ForwardEulerIntegrator(Game game)

            : base(game) { }

    }

}

Этот класс в основном наследовался от абстрактного класса Integrator.

Мы теперь создаем переопределенный метод с именем
Integrate, и используем формулы 6 и 7 данные выше, чтобы получить новую скорость и позицию соответственно. Эти новые атрибуты обновлены в данном SimObject.

(метод класса в ForwardEulerIntegrator.cs)
        public
override
void Integrate(Vector3 acceleration, SimObject simObject)

        {

            //calculate new position using the velocity at current time

            simObject.CurrPosition += simObject.CurrVelocity * fixedTimeStep;
            //calculate new velocity using the acceleration at current time

            simObject.CurrVelocity += acceleration * fixedTimeStep;

        }

  1. Класс Моделирования

У нас теперь есть все необходимые классы, для осуществления алгоритма моделирования из Секции 1.1. Мы упакуем алгоритм моделирования в класс Моделирования так, чтобы было более просто создать другие виды моделирований в более поздних главах.

Создайте новую папку под названием Simulations в папке SoftBody, и затем добавьте новый файл класса по имени Simulation.cs.

(Simulation.cs)
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using SkeelSoftBodyPhysicsTutorial.SoftBody.ForceGenerators;

using SkeelSoftBodyPhysicsTutorial.SoftBody.Integrators;

using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.Simulations

{

    public
class
Simulation

    {

        protected
Game game;

        protected
List<SimObject> simObjects = new
List<SimObject>();

        protected
List<ForceGenerator> globalForceGenerators = new
List<ForceGenerator>();

        protected
List<Spring> springs = new
List<Spring>();

        protected
Integrator integrator;

        public
List<SimObject> SimObjects

        {

            get { return simObjects; }

            set { simObjects = value; }

        }
        public
Integrator Integrator

        {

            get { return integrator; }

            set { integrator = value; }

        }
        //——————————————————————
        public Simulation(Game game)

        {

            this.game = game;
            //create a default integrator

            this.integrator = new
ForwardEulerIntegrator(game);

        }

    }

}

Из алгоритма, определенного в Секции 1.1, мы знаем, что мы должны производить итерацию через список объектов моделирования, сил и также использовать интегратор. Таким образом класс Simulation следит за:

— Списком SimObjects: Это — объекты, которые находятся в системе моделирования. У активных есть движение, определенное системой моделирования, в то время как пассивные не двигаются.

— Списком ForceGenerators: Это — глобальные источники силы, такие как Сила тяжести и Окружающая среда. Они будут применены ко всем SimObjects в системе.

— Список Springs: Это существующие локальные источники силы, которые создают силы, действующие только на прикрепленный SimObjects.

— Интегратор: используется, чтобы интегрировать ускорение SimObjects, чтобы получить позиции/скорости для следующего временного шага.

Мы также создадим некоторые методы, чтобы добавить Springs, ForceGenerators и SimObjects в систему моделирования:

(метод класса Simulation.cs)
        public
void AddSpring(float stiffness, float damping, SimObject simObjA, SimObject simObjB)

        {

            Spring spring = new
Spring(stiffness, damping, simObjA, simObjB);

            springs.Add(spring);

        }
        public
void AddSimObject(SimObject simObject)

        {

            simObjects.Add(simObject);

        }
        public
void AddGlobalForceGenerator(ForceGenerator forceGenerator)

        {

            globalForceGenerators.Add(forceGenerator);

        }

Теперь мы создаем метод Update, который осуществит алгоритм моделирования физики пошагово.

(метод класса в Simulation.cs)
        public
virtual
void Update(GameTime gameTime)

        {
        }

Во-первых, мы суммируем все силы. Сделаем это, выполняя итерации в каждом Spring, чтобы суммировать все локальные силы упругости(spring forces), и применяя глобальные силы к каждому SimObject.

(внутри Update() в Simulation.cs)
            //sum all local forces

            foreach (Spring spring in springs)

            {

                spring.ApplyForce(null);  //no need to specify any simObj

            }
            //sum all global forces acting on the objects

            foreach (SimObject simObject in simObjects)

            {

                if (simObject.SimObjectType == SimObjectType.ACTIVE)

                {

                    foreach (ForceGenerator forceGenerator in globalForceGenerators)

                    {

                        forceGenerator.ApplyForce(simObject);

                    }

                }

            }

Затем, мы находим ускорение, деля результирующую силу на массу объекта. Мы затем просим назначенный интегратор интегрировать ускорение.

(снаружи Update() в Simulation.cs)
        Vector3 acceleration;
(внутри Update() в Simulation.cs)
            foreach (SimObject simObject in simObjects)

            {

                if (simObject.SimObjectType == SimObjectType.ACTIVE)

                {

                    //find acceleration

                    acceleration = simObject.ResultantForce / simObject.Mass;
                    //integrate

                    integrator.Integrate(acceleration, simObject);

                }

            }

Теперь мы обновляем объект, прося, чтобы SimObject обновил себя.

(внутри Update() in Simulation.cs)
            //update object

            foreach (SimObject simObject in simObjects)

            {

                simObject.Update(gameTime);

            }

Припомните, что класс SimModel осуществил Update, назначая позицию GameModel для атрибута CurrPosition. Это по существу обновляет позицию модели, которую мы видим на экране согласно заново рассчитанной CurrPosition.

Наконец, мы сбрасываем силы, действующие на SimObjects так, чтобы не было никаких сил в накапливаемом ResultantForce в начале следующего временного шага.

(внутри Update() в Simulation.cs)
            //reset forces on sim objects

            foreach (SimObject simObject in simObjects)

            {

                if (simObject.SimObjectType == SimObjectType.ACTIVE)

                {

                    simObject.ResetForces();

                }

            }

1.6 Использование Новых Классов для Создания пружинного моделирования(Spring Simulation).

Теперь давайте создадим моделирование в основном игровом классе, используя все классы, которые у нас есть. Если Вы посмотрите на Game1.cs,в проектных файлах, вы увидите, что это довольно похоже на версию шаблона, предоставленную автоматически XNA Game Studio, когда Вы создаете новый проект XNA. Единственное различие — то, что все ненужные комментарии были удалены, и что необходимые компоненты были включены (обратитесь к главе по «Основным Кодам» для подробностей).

Мы сначала создадим пустой метод для инициализации сцены:

(члены класса в Game1.cs, дополнение к коду жирным шрифтом)
        protected
override
void Initialize()

        {

            InitSpringScene();

            base.Initialize();

        }

        private
void InitSpringScene()

        {

        }

Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s