Yukarıda açıklanan davranış göz önüne alındığında, 2D katı cisim fiziği için iyi bir adaydır, belki de onsuz yapılabilir, ancak mükemmel olması zor olacaktır. Bu örnekte pymunk kullanıyorum çünkü ona aşinayım ama aynı kavramlar diğer kütüphanelerle de çalışacak.
Sahnenin fareyi temsil edecek kinematik bir gövdesi vardır ve daireler başlangıçta statik cisimlerle temsil edilir. Bir daire seçiliyken dinamik bir gövdeye geçer ve sönümlü bir yay ile fareyle sınırlandırılır. Konumu, alan her zaman aşımı aralığında belirli bir zaman adımıyla güncellendikçe güncellenir.
Öğe aslında aynı şekilde hareket ettirilmez. ItemIsMovable
bayrak etkin değil, yani artık fare ile anında hareket etmiyor. Çok yakın ama küçük bir gecikme var, ancak çarpışmalara nasıl tepki verdiğini daha iyi görmek için bunu tercih edebilirsiniz. (Yani, parametreleri ayarlamak daha hızlı/**benden fare yaklaşın için iyi bile).
Öte yandan, çarpışmalar mükemmel bir şekilde ele alınır ve zaten diğer şekil türlerini destekleyecektir.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import pymunk
class Circle(QGraphicsEllipseItem):
def __init__(self, r, **kwargs):
super().__init__(-r, -r, r * 2, r * 2, **kwargs)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.static = pymunk.Body(body_type=pymunk.Body.STATIC)
self.circle = pymunk.Circle(self.static, r)
self.circle.friction = 0
mass = 10
self.dynamic = pymunk.Body(mass, pymunk.moment_for_circle(mass, 0, r))
self.updatePos = lambda: self.setPos(*self.dynamic.position, dset=False)
def setPos(self, *pos, dset=True):
super().setPos(*pos)
if len(pos) == 1:
pos = pos[0].x(), pos[0].y()
self.static.position = pos
if dset:
self.dynamic.position = pos
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
space = self.circle.space
space.remove(self.circle.body, self.circle)
self.circle.body = self.dynamic if value else self.static
space.add(self.circle.body, self.circle)
return super().itemChange(change, value)
def paint(self, painter, option, widget):
option.state &= ~QStyle.State_Selected
super().paint(painter, option, widget)
class Scene(QGraphicsScene):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.space = pymunk.Space()
self.space.damping = 0.02
self.body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
self.space.add(self.body)
self.timer = QTimer(self, timerType=Qt.PreciseTimer, timeout=self.step)
self.selectionChanged.connect(self.setConstraint)
def setConstraint(self):
selected = self.selectedItems()
if selected:
shape = selected[0].circle
if not shape.body.constraints:
self.space.remove(*self.space.constraints)
spring = pymunk.DampedSpring(
self.body, shape.body, (0, 0), (0, 0),
rest_length=0, stiffness=100, damping=10)
spring.collide_bodies = False
self.space.add(spring)
def step(self):
for i in range(10):
self.space.step(1 / 30)
self.selectedItems()[0].updatePos()
def mousePressEvent(self, event):
super().mousePressEvent(event)
if self.selectedItems():
self.body.position = event.scenePos().x(), event.scenePos().y()
self.timer.start(1000 / 30)
def mouseMoveEvent(self, event):
super().mouseMoveEvent(event)
if self.selectedItems():
self.body.position = event.scenePos().x(), event.scenePos().y()
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
self.timer.stop()
def addCircle(self, x, y, radius):
item = Circle(radius)
item.setPos(x, y)
self.addItem(item)
self.space.add(item.circle.body, item.circle)
return item
if __name__ == '__main__':
app = QApplication(sys.argv)
scene = Scene(0, 0, 1000, 800)
for i in range(7, 13):
item = scene.addCircle(150 * (i - 6), 400, i * 5)
item.setBrush(Qt.GlobalColor(i))
view = QGraphicsView(scene, renderHints=QPainter.Antialiasing)
view.show()
sys.exit(app.exec_())
**Aşağıdakileri ayarlayabilir:
- İlkbahar
stiffness
ve damping
- Vücut
mass
ve moment
eylemsizlik
- Uzay
damping
Space.step
zaman adımı / QTimer zaman aşımı başına kaç çağrı
- QTimer
interval