Biorąc pod uwagę zachowanie opisane powyżej, to dobry kandydat dla 2D fizyki ciała stałego, możliwe, że bez tego można się obejść, ale byłoby to trudne do osiągnięcia doskonałości. Używam pymunk w tym przykładzie, ponieważ znam go, ale te same koncepcje będą pracować z innymi bibliotekami.
Scena ma кинематическое ciało do reprezentowania myszy, a kręgi początkowo prezentowane są statycznymi obiektami. Przy wyborze koła przełączy się w dynamiczne ciało i utrzymuje się za pomocą myszy dampingowanej sprężyny. Jego pozycja jest aktualizowana w miarę aktualizacji na przestrzeni określonego czasu krok w każdym przedziale czasu oczekiwania.
Element jest w rzeczywistości porusza się nie tak, jak ItemIsMovable
flaga nie jest włączony, co oznacza, że nie porusza się natychmiast za pomocą myszy. To bardzo blisko, ale jest małe opóźnienie, ale można zdecydować się na to, aby lepiej widzieć, jak on reaguje na uderzenia. (Nawet w tym przypadku można dokładnie skonfigurować ustawienia, aby poruszał się szybciej/bliżej myszy, niż ja**).
Z drugiej strony, zderzenia są przetwarzane idealnie i już będą wspierać inne rodzaje kształtów.
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_())
**Można skonfigurować następujące ustawienia:
- Wiosna
stiffness
i damping
- Ciało
mass
i moment
inercja
- Kosmos
damping
Space.step
krok czasu / ile połączeń za timeout QTimer
- QTimer
interval