Vzhľadom na správanie popísané vyššie, je to dobrý kandidát pre 2D pevné telo fyziky, možno sa to dá urobiť bez toho, ale bolo by ťažké sa dostať perfektné. Ja používam pymunk v tomto príklade, pretože som oboznámený s ním, ale tie isté pojmy, bude pracovať s inými knižnicami.
Na scéne sa kinematická telo predstavujú myši a kruhy sú zastúpené statické orgány spočiatku. Pri kruhu je vybrali to prepne na dynamické telo a je fixovaný na myši a tlmené jar. Jeho poloha sa aktualizuje ako priestor je aktualizovaný v danom časovom kroku na každý časový interval, interval.
Tovar nie je pohybovali rovnakým spôsobom ako ItemIsMovable
vlajky nie je povolené, čo znamená, že už sa pohybuje okamžite s myšou. Je to veľmi blízko, ale tam je malé oneskorenie, hoci si to možno radšej to lepšie vidieť, ako reaguje na zrážok. (Aj tak môžete doladiť parametre majú to pohyb rýchlejší/bližšie k myši, než som urobil**).
Na druhej strane, kolízie sú spracované perfektne a už bude podporovať iné druhy tvarov.
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_())
**Môžete upraviť nasledovné:
- Jar
stiffness
a damping
- Telo
mass
a moment
zotrvačnosti
- Priestor
damping
Space.step
časový krok / počet hovorov za QTimer timeout
- QTimer
interval