Wie man MS Male Klon mit Python erstellen und pygame
Frage
Wie ich es sehe, gibt es zwei Möglichkeiten Maus-Ereignisse zu behandeln, ein Bild zu zeichnen.
Die erste ist, wenn sich die Maus bewegt, zu detektieren und eine Linie zu ziehen, wo die Maus ist, gezeigt hier . Dies ist jedoch das Problem mit, dass mit einer großen Pinselgröße, erscheinen viele Lücken zwischen dem einzelnen „Linie“, die nicht gerade ist, da es die Linie der Strichgröße wird mit dicken Linien erstellen.
Der andere Weg ist Kreise zu ziehen, wenn die Maus bewegt, wie gezeigt wird, hier . Das Problem dabei ist, dass Lücken zwischen den einzelnen Kreis angezeigt, wenn die Maus bewegt sich schneller als der Computer-Maus-Eingang erkennt.
Hier ist ein Screenshot mit meinen Problemen mit den beiden:
Was ist der beste Weg, um eine Bürste wie MS Paint, mit einer anständig großen Pinselgröße ohne Lücken im Takt der Linie oder ohne Lücken zwischen dem einzelnen Kreis?
implementierenLösung
Warum nicht beides tun?
Zeichnen Sie einen Kreis an jedem Endpunkt und eine Linie zwischen den beiden.
Bearbeiten rofl, konnte einfach nicht aufhören mich.
Eigentlich wollen Sie nicht pygame.draw.line
verwenden, weil es betrügt. Es füllt eine 1-Pixel-breite Zeile oder Spalte (je nach Anstellwinkel) von Pixeln. Wenn Sie an einem etwa senkrechten Winkel gehen Sie, 0 ° oder 90 °, ist dies kein Problem, aber bei 45 ist, erhalten Sie eine Art von string bean merken Effekt.
Die einzige Lösung ist, einen Kreis an jedem Pixel der Abstand zu ziehen. Hier ...
import pygame, random
screen = pygame.display.set_mode((800,600))
draw_on = False
last_pos = (0, 0)
color = (255, 128, 0)
radius = 10
def roundline(srf, color, start, end, radius=1):
dx = end[0]-start[0]
dy = end[1]-start[1]
distance = max(abs(dx), abs(dy))
for i in range(distance):
x = int( start[0]+float(i)/distance*dx)
y = int( start[1]+float(i)/distance*dy)
pygame.draw.circle(srf, color, (x, y), radius)
try:
while True:
e = pygame.event.wait()
if e.type == pygame.QUIT:
raise StopIteration
if e.type == pygame.MOUSEBUTTONDOWN:
color = (random.randrange(256), random.randrange(256), random.randrange(256))
pygame.draw.circle(screen, color, e.pos, radius)
draw_on = True
if e.type == pygame.MOUSEBUTTONUP:
draw_on = False
if e.type == pygame.MOUSEMOTION:
if draw_on:
pygame.draw.circle(screen, color, e.pos, radius)
roundline(screen, color, e.pos, last_pos, radius)
last_pos = e.pos
pygame.display.flip()
except StopIteration:
pass
pygame.quit()
Andere Tipps
Nicht bei jedem Schleifenschritt Blitten die Geschwindigkeit der Zeichnung verbessern (diesen Code aus dem vorherigen ermöglichen angepasst mit Verzögerung Problem auf meinem Rechner entfernen)
import pygame, random
screen = pygame.display.set_mode((800,600))
draw_on = False
last_pos = (0, 0)
color = (255, 128, 0)
radius = 10
def roundline(srf, color, start, end, radius=1):
dx = end[0]-start[0]
dy = end[1]-start[1]
distance = max(abs(dx), abs(dy))
for i in range(distance):
x = int( start[0]+float(i)/distance*dx)
y = int( start[1]+float(i)/distance*dy)
pygame.display.update(pygame.draw.circle(srf, color, (x, y), radius))
try:
while True:
e = pygame.event.wait()
if e.type == pygame.QUIT:
raise StopIteration
if e.type == pygame.MOUSEBUTTONDOWN:
color = (random.randrange(256), random.randrange(256), random.randrange(256))
pygame.draw.circle(screen, color, e.pos, radius)
draw_on = True
if e.type == pygame.MOUSEBUTTONUP:
draw_on = False
if e.type == pygame.MOUSEMOTION:
if draw_on:
pygame.display.update(pygame.draw.circle(screen, color, e.pos, radius))
roundline(screen, color, e.pos, last_pos, radius)
last_pos = e.pos
#pygame.display.flip()
except StopIteration:
pass
pygame.quit()
Für das erste Problem, benötigen Sie einen Hintergrund haben, auch wenn es nur eine Farbe. Ich hatte das gleiche Problem mit einem Replika-Pong-Spiel, das ich gemacht. Hier ist ein Beispiel für eine Replik Malprogramm ich gemacht, klicken Sie links zu ziehen, klicken Sie rechts zu löschen, klicken Sie auf das Farbbild mit einer Farbe zu wählen, und die Aufwärtstaste Bildschirm zu löschen:
import os
os.environ['SDL_VIDEO_CENTERED'] = '1'
from pygamehelper import *
from pygame import *
from pygame.locals import *
from vec2d import *
from math import e, pi, cos, sin, sqrt
from random import uniform
class Starter(PygameHelper):
def __init__(self):
self.w, self.h = 800, 600
PygameHelper.__init__(self, size=(self.w, self.h), fill=((255,255,255)))
self.img= pygame.image.load("colors.png")
self.screen.blit(self.img, (0,0))
self.drawcolor= (0,0,0)
self.x= 0
def update(self):
pass
def keyUp(self, key):
if key==K_UP:
self.screen.fill((255,255,255))
self.screen.blit(self.img, (0,0))
def mouseUp(self, button, pos):
pass
def mouseMotion(self, buttons, pos, rel):
if pos[1]>=172:
if buttons[0]==1:
#pygame.draw.circle(self.screen, (0,0,0), pos, 5)
pygame.draw.line(self.screen, self.drawcolor, pos, (pos[0]-rel[0], pos[1]-rel[1]),5)
if buttons[2]==1:
pygame.draw.circle(self.screen, (255,255,255), pos, 30)
if buttons[1]==1:
#RAINBOW MODE
color= self.screen.get_at((self.x, 0))
pygame.draw.line(self.screen, color, pos, (pos[0]-rel[0], pos[1]-rel[1]), 5)
self.x+= 1
if self.x>172: self.x=0
else:
if pos[0]<172:
if buttons[0]==1:
self.drawcolor= self.screen.get_at(pos)
pygame.draw.circle(self.screen, self.drawcolor, (250, 100), 30)
def draw(self):
pass
#self.screen.fill((255,255,255))
#pygame.draw.circle(self.screen, (0,0,0), (50,100), 20)
s = Starter()
s.mainLoop(40)
Hier ist eine vereinfachte Version des Matthäus Beispiel die leider nicht runnable.
Wenn die Maus bewegt wird, pygame.MOUSEMOTION
Ereignisse an die Ereigniswarteschlange hinzugefügt werden, welche die Position und die relative Bewegung enthält. Sie können diese verwenden, um die vorherige Position zu berechnen und dann die beiden Punkte zu pygame.draw.line
passieren.
pygame.MOUSEMOTION
Ereignisse haben auch ein buttons
Attribut, das Sie verwenden können, um zu überprüfen, welche Maustaste ist zur Zeit nach unten.
import os
import random
import pygame as pg
class App:
def __init__(self):
os.environ['SDL_VIDEO_CENTERED'] = '1'
pg.init()
self.w, self.h = 800, 600
self.screen = pg.display.set_mode((self.w, self.h))
self.screen.fill(pg.Color('white'))
self.clock = pg.time.Clock()
self.drawcolor = (0, 0, 0)
def mainloop(self):
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 2: # Color picker (middle mouse button).
self.drawcolor = self.screen.get_at(pos)
# Pick a random color.
# self.drawcolor = [random.randrange(256) for _ in range(3)]
elif event.type == pg.MOUSEMOTION:
pos, rel = event.pos, event.rel
if event.buttons[0]: # If the left mouse button is down.
# Draw a line from the pos to the previous pos.
pg.draw.line(self.screen, self.drawcolor, pos, (pos[0]-rel[0], pos[1]-rel[1]), 5)
elif event.buttons[2]: # If the right mouse button is down.
# Erase by drawing a circle.
pg.draw.circle(self.screen, (255, 255, 255), pos, 30)
pg.display.flip()
self.clock.tick(30)
if __name__ == '__main__':
app = App()
app.mainloop()
pg.quit()