Étant donné le comportement décrit ci-dessus, il est un bon candidat pour la 2D physique de corps rigide, peut-être, il peut être fait sans, mais il serait difficile d'obtenir la perfection. Je suis à l'aide de pymunk dans cet exemple parce que je suis familier avec elle, mais les mêmes concepts de travailler avec d'autres bibliothèques.
La scène a une cinématique corps pour représenter la souris et les cercles sont représentés par des organismes d'abord. Alors qu'un cercle est sélectionné, il passe à un organisme dynamique et est contraint à la souris par une amortie de printemps. Sa position est mise à jour à mesure que l'espace est mis à jour par un pas de temps sur chaque intervalle de délai d'attente.
L'article n'est pas réellement déplacé de la même manière que le ItemIsMovable
indicateur n'est pas activé, ce qui signifie qu'il ne bouge plus instantanément avec la souris. Il est très proche, mais il y a un petit retard, bien que vous préférerez peut-être de mieux voir comment il réagit à des collisions. (Même si, vous pouvez affiner les paramètres de l'avoir à se déplacer plus vite ou plus proche de la souris que j'ai fait**).
D'autre part, les collisions sont gérées parfaitement et va déjà prendre en charge d'autres types de formes.
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_())
**Pouvez régler les éléments suivants:
- Printemps
stiffness
et damping
- Corps
mass
et moment
d'inertie
- L'espace
damping
Space.step
pas de temps / combien d'appels par QTimer délai d'attente
- QTimer
interval