Магниты
Итак, продолжим нашу экскурсию в удивительный мир PhysX
Сегодня тема пойдёт о магнитах. Штука эта намного полезнее, чем пожет показаться на первый взгляд. Почти в любой игре, в которой используется физика используются и магниты. Любые эффекты, типа телекинеза, гравипушки, чёрной дыры и даже взрывов - это всё дело рук магнитов
Так что постараемся подробнее разобраться в принципах их действия.
Функций для работы с магнитами не много, но все полезные и часто используемые. Для начала научимся создавать магнит:
pxCreateMagnet(minforce#, middleforce#, maxforce#)
где:
minforce# - минимальное усилие
middleforce# - среднее усилие
maxforce# - максимальное усилие
Эта функция создаёт магнит с тремя значениями силы. Сила считается по формуле :
Force = minforce + middleforce/distance + maxforce/(distance*distance). Установка одного из значений в ноль убирает соответствующее слагаемое. Сила считается между центром магнита и центром тела. Может быть направлена как к магниту(притягивает), так и от него(отталкивает), в зависимости от знака силы. Действует на все тела в сцене.
pxMagnetActivate(mdata%, mmode%, fmode%)
где:
mdata% - магнит
mmode% - тип магнита
fmode% - тип силы
mmode - тип магнита.
1 - магнит не имеющий области остановки
2 - с областью остановки
3 - тела притягиваются не по силе, а по скоросте (формула для скорости аналогична формуле по силе)
fmode - тип силы (как в pxBodyAddForce и подобных)
Данная функция активирует магнит. Её необходимо запускать в каждом цикле программы, если нужен постоянный магнит (более подробно мы рассмотрим дальше).
И собсно функция для позиционирования магнита:
pxMagnetSetPosition(mdata%, pos_x#, pos_y#, pos_z#)
где:
mdata% - магнит
pos_x#, pos_y#, pos_z# - координаты
Тут я думаю все понятно. Итак, попробуем создать обычный магнит. Делаем свет, камеру, физический мир, функции для создания, обновления кубика и тд. :
Graphics3D 800,600,32,2
SetBuffer BackBuffer()
SeedRnd MilliSecs()
SetFont LoadFont(Arial,12)
; Инициализация физики
pxCreateWorld(1,"")
pxSetGravity(0,-10,0)
; Камера
cam=CreateCamera()
PositionEntity cam,15,10,-25
RotateEntity cam,20,35,0
; Свет
light=CreateLight()
PositionEntity light,10,10,-10
; Плоскость
plane=CreatePlane()
EntityAlpha plane,0.8
EntityColor plane,150,255,255
CreateMirror()
While Not KeyDown(1)
pxRenderPhysic(60,0)
UpdatepxCubes()
RenderWorld
Flip
Wend
End
Type pxCube
Field mesh
Field body
End Type
Function CreatepxCube(x#,y#,z#)
pxC.pxCube = New pxCube
pxC\mesh = CreateCube()
pxC\body = pxBodyCreateCube(1,1,1,1)
pxBodySetPosition pxC\body,x,y,z
End Function
Function UpdatepxCubes()
For pxC.pxCube = Each pxCube
pxBodySetEntity pxC\mesh, pxC\body
Next
End Function
Function DeletepxCubes()
For pxC.pxCube = Each pxCube
FreeEntity pxC\mesh
pxDeleteBody pxC\body
Delete pxC
Next
End Function
Теперь давайте накидаем несколько кубиков и создадим управляемый магнит. Первое - накидываем кучку кубиков (перед циклом):
For i=0 To 15
CreatepxCube(Rnd(-10,10),15,Rnd(-10,10))
Next
А теперь создаём магнит (тоже до цикла):
mag1=pxCreateMagnet(1,5,10) ; создаём магнит
pxMagnetSetMaxRadius(mag1, 15) ; устанавливаем макс радиус действия
pxMagnetSetPosition(mag1,-10,0,0) ; устанавливаем позицию
И в цикле активируем магнит:
pxMagnetActivate(mag1,0,0)
Запускаем и любуемся
:
Теперь сделаем, чтобы магнит можно было двигать стрелочками на клаве и чтоб он активировался только когда держим пробел. Это пишем до цикла (создаём синий шар, чтоб мы могли видеть перемещение магнита):
sp =CreateSphere ()
ScaleEntity sp,0.5,0.5,0.5
EntityColor sp,0,0,255
x#=-10
z#=0
А это уже в цикле (управление магнитом)
If KeyDown (205) x# = x#+0.2
If KeyDown (203) x# = x#-0.2
If KeyDown (200) z# = z#+0.2
If KeyDown (20
z# = z#-0.2
If KeyDown(57) pxMagnetActivate(mag1,0,0)
pxMagnetSetPosition(mag1, x, y, z)
PositionEntity sp, x,y,z
[/code]
Попробуем сделать, чтобы по enter магнит менял полярность (отталкивал) и шарик менял цвет. В итоге получаем:
x#=-10
z#=0
p%=1
While Not KeyDown(1)
If KeyDown (205) x# = x#+0.2
If KeyDown (203) x# = x#-0.2
If KeyDown (200) z# = z#+0.2
If KeyDown (208) z# = z#-0.2
If KeyDown(57) pxMagnetActivate(mag1,0,0)
If KeyHit(28) p=-p
pxMagnetSetMinForce(mag1, 1*p)
pxMagnetSetMidForce(mag1, 5*p)
pxMagnetSetMaxForce(mag1, 10*p)
pxMagnetSetPosition(mag1, x, y, z)
PositionEntity sp, x,y,z
If p=1
EntityColor sp,0,0,255
Else
EntityColor sp,255,0,0
EndIf
pxRenderPhysic(60,0)
UpdatepxCubes()
RenderWorld
Flip
Wend
Теперь давайте сделаем взрыв. Создадим новый магнит (ниже первого):
explode=pxCreateMagnet(0,-20,-50)
pxMagnetSetMaxRadius(mag1, 15)
и сферу для его обозначения:
explode_sp=CreateSphere()
EntityColor explode_sp,250,200,100
В цикле пропишем активацию взрыва по кнопке "e" и анимацию сферы:
If KeyHit(18) And boom=0
pxMagnetActivate(explode,0,1)
boom=1
EndIf
If boom=1
rad=rad+2
ScaleEntity explode_sp,rad,rad,rad
If rad>200 boom=0
Else
rad=Abs(Sin(MilliSecs()*0.2))+0.5
ScaleEntity explode_sp,rad,rad,rad
EndIf
Естественно, переменные boom и rad лучше обьявить в начале программы (boom%=0 rad#=0). Добавим рестарт появления кубиков по кнопке Tab:
If KeyHit(15)
DeletepxCubes()
For i=0 To 15
CreatepxCube(Rnd(-10,10),15,Rnd(-10,10))
Next
EndIf
И теперь мы сможем управлять и взрывом, и магнитом плюс это наглядно видно:
Ниже приведу список функций, которые интуитивно понятны и не требуют разьяснения:
pxMagnetSetMinRadius(mdata%,minradius#)
pxMagnetSetMaxForce(mdata%,maxforce#)
pxMagnetSetMidForce(mdata%,middleforce#)
pxMagnetSetMinForce(mdata%,minforce#)
Функции для получения (возврата) значений:
pxMagnetGetPositionX(x#)
pxMagnetGetPositionY(y#)
pxMagnetGetPositionZ(z#)
pxMagnetGetMaxRadius(maxradius#)
pxMagnetGetMinRadius(minradius#)
pxMagnetGetMaxForce(maxforce#)
pxMagnetGetMidForce(middleforce#)
pxMagnetGetMinForce(minforce#)
И собсно функция удаления магнита:
Внимание! Функция удаления физического мира pxDestroyWorld() не удаляет магниты.
И последнее, что я хотел рассказать. Те, кто уже в уме представляет себе, как он будет делать в своей игре взрывы и телекинез
, спросит у меня - а как же быть, если в игре не нужно притягивать/отталкивать все предметы, а наоборот - лишь единичные? А с помощью
pxBodySetFlagMagniteble(body%, stat%) и масок, отвечу я вам. Остановимся поподробнее. Итак, функция
pxBodySetFlagMagniteble() позволяет нам ставить любому телу флаг (stat), т.е. если stat=0 - магниты не влияют на тело и stat=1 - соответственно влияют. Но такой способ не всегда удобен. Потому существуют так называемые маски:
pxMagnetSetMask(mdata%,mask%)
pxBodySetMagnetMask(body%,mask%)
Как работают маски? Мы задаём маску любому телу и любому магниту. Если маски тела и магнита равны - магнит действует на тела с этой маской. Если маска тела и мегнита не равны - магнит действует на тела с меньшей маской.
Изменим немного функцию создания кубиков, добавим случайное разделение на металл(белый с маской 2) и неметалл(красный с маской 1):
Function CreatepxCube(x#,y#,z#)
pxC.pxCube = New pxCube
pxC\mesh = CreateCube()
pxC\body = pxBodyCreateCube(1,1,1,1)
pxBodySetPosition pxC\body,x,y,z
Metall=Rand(0,1)
If Metall
EntityColor pxC\mesh,230,232,215
pxBodySetMagnetMask(pxC\body, 2) ;bin(10)
Else
EntityColor pxC\mesh,128,64,64
pxBodySetMagnetMask(pxC\body, 1) ;bin(01)
EndIf
End Function
Теперь поставим маски нашим магнитам:
pxMagnetSetMask(mag1,2)
pxMagnetSetMask(explode,3)
И наблюдаем. Получаеться у нас магнитом притягиваются только белые кубики (т.к. маски одинако=2), а взрыв берёт всех (т.к. маска взрыва 3 не равна не 2, ни 1 и больше их).
Полный код как обычно в аттаче