Python 및 Pygame으로 MS 페인트 클론을 만드는 방법
문제
내가 보았 듯이, 마우스 이벤트를 처리하여 그림을 그리는 두 가지 방법이 있습니다.
첫 번째는 마우스가 움직일 때를 감지하고 마우스가있는 위치에 선을 그리는 것입니다. 여기. 그러나이 문제는 브러시 크기가 크면 라인의 스트로크 크기를 사용하여 두꺼운 선을 생성하기 때문에 똑바로 아닌 각 "선"사이에 많은 간격이 나타납니다.
다른 방법은 표시된대로 마우스가 움직일 때 원을 그리는 것입니다. 여기. 이 문제는 컴퓨터가 마우스 입력을 감지하는 것보다 마우스가 더 빨리 움직이면 각 원 사이에 간격이 나타납니다.
다음은 다음과 같은 문제에 대한 스크린 샷입니다.
MS Paint 's와 같은 브러시를 구현하는 가장 좋은 방법은 무엇입니까? 선의 스트로크에 틈이 없거나 각 원 사이의 간격이없는 괜찮은 빅 브러시 크기가있는 가장 좋은 방법은 무엇입니까?
해결책
왜 둘 다하지 않습니까?
각 엔드 포인트에서 원과 둘 사이의 선을 그립니다.
편집하다 Rofl, 그냥 내 자신을 막을 수 없었습니다.
사실, 당신은 사용하고 싶지 않습니다 pygame.draw.line
속임수 때문입니다. 픽셀의 1 픽셀 와이드 행 또는 열 (공격 각도에 따라)을 채 웁니다. 대략 수직 각도, 0 Deg 또는 90 도로 가면 문제가되지 않지만 45 대에 일종의 끈 콩 효과.
유일한 해결책은 모든 픽셀의 거리에서 원을 그리는 것입니다. 여기...
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()
다른 팁
각 루프 단계에서 블리팅되지 않으면 도면의 속도가 향상 될 수 있습니다 (이전 코드를 사용하여 이전 코드를 사용하면 내 컴퓨터에서 지연 문제를 제거 할 수 있습니다)
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()
첫 번째 문제의 경우 색상 일지라도 배경이 필요합니다. 나는 내가 만든 복제판 게임에서도 같은 문제가 있었다. 다음은 내가 만든 복제 페인트 프로그램의 예입니다. 왼쪽 클릭하고 오른쪽 클릭하고 마우스 오른쪽 버튼을 클릭하고 색상 이미지를 클릭하여 색상을 선택하고 화면을 지우려면 버튼을 확인하십시오.
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)
다음은 Matthew 's의 단순화 된 버전입니다 예시 불행히도 실행할 수 없습니다.
마우스가 움직일 때 pygame.MOUSEMOTION
이벤트는 위치와 상대적인 움직임을 포함하는 이벤트 큐에 추가됩니다. 이것들을 사용하여 이전 위치를 계산 한 다음 두 지점을 전달할 수 있습니다. pygame.draw.line
.
pygame.MOUSEMOTION
이벤트에는 또한 이벤트에도 있습니다 buttons
현재 다운 된 마우스 버튼을 확인하는 데 사용할 수있는 속성.
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()