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

1.2.2 SimModel

Основанный на абстрактном классе SimObject, мы создадим наш первый класс объекта моделирования — SimModel. Он предназначен для того, чтобы прикрепить атрибуты моделирования к модели, которую мы загрузим из файла, и обновлять позицию этой загруженной модели в каждом временном шаге. В папке SimObjects, создайте новый файл класса по имени SimModel.cs.

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

using SkeelSoftBodyPhysicsTutorial.Main;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects

{

    public
sealed
class
SimModel : SimObject

    {

        private
GameModel model;

        public
GameModel Model

        {

            get { return model; }

            set { model = value; }

        }
        //——————————————————————
        public SimModel(GameModel model, float mass, SimObjectType simObjectType)

            : base(mass, simObjectType)

        {

            this.model = model;

            this.currPosition = model.Translate;

            this.prevPosition = currPosition;

        }

    }

}

По существу мы передаем в GameModel (один из классов предоставленных в основном коде), его массу и его SimObjectType, когда мы создаем экземпляр класса SimModel. currPosition и prevPosition переменные обновляются текущим положением GameModel.

Так как метод Update определен как абстрактный в базовом классе, мы должны будем переопределить метод. Все, что мы должны сделать для этого метода — это обновить позицию GameModel согласно currPosition, которая будет повторно вычисляться в каждом временном шаге. Это по существу синхронизирует позицию GameModel с хранимой позицией.

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

        {

            this.model.Translate = this.currPosition;

        }

Это — все, что мы должны сделать для этого класса, так как остальное наследуется от базового класса.

  1. Классы Генератора Силы

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

1.3.1 Интерфейс ForceGenerator

Нам следует начать с создания интерфейса ForceGenerator и сделать его обязательным для производных классов, чтобы обеспечить выполнение метода ApplyForce.

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

(ForceGenerator.cs)
using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.ForceGenerators

{

    public
interface
ForceGenerator

    {

        void ApplyForce(SimObject simObject);

    }

}

Мы хотим, чтобы производные классы, когда выполняют метод ApplyForce, использовали свойства SimObject (такие как масса, положение, скорость и пр.), чтобы вычислить соответствующую силу и добавить полученное к ResultantForce.

1.3.2 Сила тяжести

Давайте продолжим и создадим наш первый и самый простой источник силы: гравитацию. Чтобы получить гравитационную силу, действующую на объект, нам нужно знать 2 вещи, а именно, массу тела и гравитационное ускорение, которое оно испытывает. Гравитационная сила может тогда просто быть найдена как

Гравитационная сила = масса * гравитационное ускорение [1]

Теперь, добавьте новый файл класса по имени Gravity.cs в папке ForceGenerators и создайте класс Силы тяжести, который наследуется от интерфейса ForceGenerator:

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

using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.ForceGenerators

{

    public
sealed
class
Gravity : ForceGenerator

    {

        private
Vector3 acceleration;

        public
Vector3 Acceleration

        {

            get { return acceleration; }

            set { acceleration = value; }

        }
        public
float AccelerationX

        {

            get { return acceleration.X; }

            set { acceleration.X = value; }

        }
        public
float AccelerationY

        {

            get { return acceleration.Y; }

            set { acceleration.Y = value; }

        }
        public
float AccelerationZ

        {

            get { return acceleration.Z; }

            set { acceleration.Z = value; }

        }
        //——————————————————————
        public Gravity()

            : this(new
Vector3(0, -9.81f, 0)) { }

        public Gravity(Vector3 acceleration)

            : base()

        {

            this.acceleration = acceleration;

        }

    }

}

Первый конструктор не присваивает ускорения, но создает ускорение -9.81 вниз по умолчанию. Это гравитационное ускорение, которое мы испытываем на Земле.

Мы теперь должны реализовать метод ApplyForce. Как описано ранее, мы должны найти результат массы объекта и гравитационного ускорения, и добавить это свойствам ResultantForce.

(метод класса в Gravity.cs)
        public
void ApplyForce(SimObject simObject)

        {

            simObject.ResultantForce += simObject.Mass * acceleration;

        }

1.3.3 Сопротивление(Medium)

Следующим мы добавим источник силы под названием Сопротивление, который представляет собой среду, в которой находится объект моделирования. Вязкость окружающей среды, в которой находится тело, определит, какую силу движения (сопротивления)(drag force) тело будет испытывать. Объект, двигающийся в меду, например, испытает, большую силу сопротивления среды, чем объект в воздухе. Чтобы упростить реализацию, мы только представим вязкость коэффициентом сопротивления(drag coefficient), который может быть произвольно откорректирован пользователем, чтобы получить, желаемый эффект сопротивления.

Добавьте файл Medium.cs в папку ForceGenerators.

(Medium.cs)
using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.ForceGenerators

{

    public
sealed
class
Medium : ForceGenerator

    {

        private
float dragCoefficient;

        public
float DragCoefficient

        {

            get { return dragCoefficient; }

            set { dragCoefficient = value; }

        }
        //———————————————————
        public Medium(float dragCoefficient)

            : base()

        {

            this.dragCoefficient = dragCoefficient;

        }

    }

}

Как обычно, мы должны реализовать метод ApplyForce. Сила сопротивления обычно пропорциональна текущей скорости тела. Чем быстрее тело двигается, тем больше сила сопротивления. Таким образом, все, что мы должны сделать, умножить наш коэффициент сопротивления на текущую скоростью моделируемого объекта:

Сила сопротивления = — коэф.сопротивления * скорость объекта [2]

Так как это — противостоящая сила, которая замедляет движение объекта, мы должны добавить — .

Реализация для этого следующая:

(метод класса в Medium.cs)
        public
void ApplyForce(SimObject simObject)

        {

            simObject.ResultantForce += — dragCoefficient * simObject.CurrVelocity;

        }

1.3.4 Сила упругости(Spring)

Мы теперь осуществляем последний источник силы — силу упругости. У этой силы есть следующие атрибуты:

Жесткость: Это определяет «упругость» пружины. Чем выше значение жесткости, тем большую силу генерируем, когда пружина будет сжата или растянута. Это заставляет пружину возвращаться к ее оригинальной форме быстрее, таким образом, создавая более жесткое поведение. Это также известно как коэффициент упругости(spring constant).

Затухание(Damping): Это определяет количество внутренних сил сопротивления, которые пружина будет испытывать. Сила сопротивления параллельна направлению силе упругости.

Длина в состоянии покоя: Это длина, которую пружина попытается достичь, подвергаясь сжатию или растяжению.

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


Рис 1: Действие сил упругости на оба конца пружины подвергающейся сжатию.

Что касается Рис. 1, сила упругости, испытанная объектом A, вычисляется как:

Сила упругости на А = — жесткость * (текущую длину – длину в состоянии покоя)* единичный вектор ВА [3]

У пружины есть своя собственная сила затухания(damping force), в дополнение к силе затухания, вызванной окружающей средой, в которой тело находится. Различие между этими двумя типами сил затухания — то, что сила затухание пружины действует только на саму пружину, в то время как сила сопротивления окружающей среды действует на движение всех объектов. Сила затухания пружины пропорциональна относительным скоростям двух объектов и обычно пишется как:

Сила затухания на А = — затухание * ((скорость А – Скорость В).единичные вектор ВА)*единичный вектор ВА [4]

Заметьте, что эта сила затухания(damping force) должна быть в направлении от B к A. Однако, разница скоростей — вектор, и может указывать не в желаемом направлении. Таким образом, мы должны привести вектор разницы скоростей к вектору направленному от B к A. Это сделано через скалярное произведение вектора разницы скоростей с единичным вектором от B к А(результатом будет модуль), и затем это все умножается на единичный вектор от B к А(результат — вектор, который нам надо). Присутствует «-«, потому что это — противостоящая сила.

Третий Закон Ньютона Движения утверждает, что для каждой действующей силы, есть равная и противоположная сила. Таким образом, сила упругости, испытываемая объектом B, является такой же, как испытываемая объектом A, но в противоположном направлении. Это относится и к силе затухания пружины.

Теперь, когда у нас есть достаточно знаний о силах упругости, добавим класс Сил упругости. Создайте новый файл класса и назовите Spring.cs в папке ForceGenerators.

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

using SkeelSoftBodyPhysicsTutorial.SoftBody.SimObjects;
namespace SkeelSoftBodyPhysicsTutorial.SoftBody.ForceGenerators

{

    public
sealed
class
Spring : ForceGenerator

    {

        private
float stiffness;

        private
float damping;

        private
float restLength;

        private
SimObject simObjectA;

        private
SimObject simObjectB;

        public
float Stiffness

        {

            get { return stiffness; }

            set { stiffness = value; }

        }
        public
float Damping

        {

            get { return damping; }

            set { damping = value; }

        }
        public
SimObject SimObjectA

        {

            get { return simObjectA; }

            set { simObjectA = value; }

        }
        public
SimObject SimObjectB

        {

            get { return simObjectB; }

            set { simObjectB = value; }

        }
        //————————————————————
        public Spring(float stiffness, float damping, SimObject simObjectA, SimObject simObjectB)

            : this(stiffness, damping, simObjectA, simObjectB, (simObjectA.CurrPosition — simObjectB.CurrPosition).Length()) { }
        public Spring(float stiffness, float damping, SimObject simObjectA, SimObject simObjectB, float restLength)

            : base()

        {

            this.stiffness = stiffness;

            this.damping = damping;

            this.simObjectA = simObjectA;

            this.simObjectB = simObjectB;

            this.restLength = restLength;

        }

    }

}

Класс Силы упругости в основном присваивает необходимые атрибуты пружины и хранит их. Он также присваивает два SimObjects, которые присоединены к каждому концу пружины. Если длина в состоянии покоя не сообщена конструктору, то он берет стартовое расстояние между двумя SimObjects.

Затем, мы реализуем метод ApplyForce:

(метод класса в Spring.cs)
        private
Vector3 direction;

        private
float currLength;

        private
Vector3 force;

        public
void ApplyForce(SimObject simObject)

        {

            //get the direction vector

            direction = simObjectA.CurrPosition — simObjectB.CurrPosition;
            //check for zero vector

            if (direction != Vector3.Zero)

            {

                //get length

                currLength = direction.Length();
                //normalize

                direction.Normalize();
                //add spring force

                force = -stiffness * ((currLength — restLength) * direction);
                //add spring damping force

                force += -damping * Vector3.Dot(simObjectA.CurrVelocity — simObjectB.CurrVelocity, direction) * direction;
                //apply the equal and opposite forces to the objects

                simObjectA.ResultantForce += force;

                simObjectB.ResultantForce += -force;

            }

        }

Осуществление метода ApplyForce немного сложнее. Мы сначала получаем вектор направления от объекта B до A, потом мы находим его длину и нормализуем. Помните, что мы должны проверить, является ли вектор не нулевым прежде, чем мы нормализуем его, или иначе у нас будет деление на нуль. С этой информацией мы можем вычислить силу упругости и силы сопротивления пружины согласно Формуле 3 и Формуле 4 соответственно. Наконец, мы добавляем эти силы к объекту A и добавляем равную, но противоположную силу к объекту B.

Обратите внимание, что мы не используем SimObject, который передали как параметр, так как мы уже сохранили оба необходимых SimObjects в классе непосредственно. Это немного странно осуществлять метод, определенный в классе интерфейса. Однако это не наносит много ущерба, и мы можем пройти с пустым указателем, когда мы вызываем этот метод для экземпляра класса Сил упругости.

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

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s