2

私がやっている小さなRPGのシャドウキャスターに取り組んでいます。

私が抱えている問題は、ゲームで使用すると、速度が遅くなり、ひどいラグが発生することです。

投稿の長さにあまり驚かないでください。これは非常に簡単ですが、コードを実行できるように、ブレゼンハムのアルゴリズムもすべて含めました。

原理は次のとおりです。 - 黒い表面を作成します - 位置と半径で光源を定義します。- Bresenham の円アルゴリズムを使用して、この位置と半径によって定義される円の円周上のすべての点を取得します。- 円周に沿った各ポイントについて、ブレゼンハムのライン アルゴリズムを使用して、光源の位置からラインを描画します。- 次に、線の点を繰り返し、画面に表示されているすべての障害物と衝突するかどうかを確認します。- 衝突がない場合は、その点を中心に半径 10 ピクセル程度の白い円を描きます。- 衝突が発生した場合は、円周に沿って次のポイントに移動します。- 最後に、黒色の透明度値が 100 で、白色の透明度が完全なサーフェス上のすべての白い円でサーフェスをブリットします。

これまでのところ、私は次のことを試みました: 遅延を減らしました: - 障害物リストを画面に表示されるものに制限します - 画面の端を障害物と見なして、見えない領域の反復を減らします。- 円の周りの 3 点と線に沿った 12 点ごとにのみ繰り返します。これは何も変更しませんでした: - 線に沿った多数の円の代わりに、光源から範囲の端または障害物に向かう楕円を使用します。問題は、楕円ごとにサーフェスを再描画してから、全体を回転させる必要があることでした。

これをより効率的にする方法について何か提案があれば、喜んでここにいたします。

Bresenham のライン アルゴリズム:

def get_line(start, end):
    """Bresenham's Line Algorithm
    Produces a list of tuples from start and end

    >>> points1 = get_line((0, 0), (3, 4))
    >>> points2 = get_line((3, 4), (0, 0))
    >>> assert(set(points1) == set(points2))
    >>> print points1
    [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
    >>> print points2
    [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
    """
    # Setup initial conditions
    x1, y1 = start
    x2, y2 = end
    dx = x2 - x1
    dy = y2 - y1

    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)

    # Rotate line
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2

    # Swap start and end points if necessary and store swap state
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True

    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1

    # Calculate error
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1

    # Iterate over bounding box generating points between start and end
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx

    # Reverse the list if the coordinates were swapped
    if swapped:
        points.reverse()
    return points

Bresenham の円アルゴリズム:

def get_circle((dx,dy),radius):
    "Bresenham complete circle algorithm in Python"
    # init vars
    switch = 3 - (2 * radius)
    points = set()
    x = 0
    y = radius
    # first quarter/octant starts clockwise at 12 o'clock
    while x <= y:
        # first quarter first octant
        points.add((x,-y))
        # first quarter 2nd octant
        points.add((y,-x))
        # second quarter 3rd octant
        points.add((y,x))
        # second quarter 4.octant
        points.add((x,y))
        # third quarter 5.octant
        points.add((-x,y))        
        # third quarter 6.octant
        points.add((-y,x))
        # fourth quarter 7.octant
        points.add((-y,-x))
        # fourth quarter 8.octant
        points.add((-x,-y))
        if switch < 0:
            switch = switch + (4 * x) + 6
        else:
            switch = switch + (4 * (x - y)) + 10
            y = y - 1
        x = x + 1
    offset_points = set()
    for pt in points:
        offset_points.add((pt[0]+dx,pt[1]+dy))

    return offset_points

def shadow_gen(shadow_surf,source,cir_pt,obstacles):
    line_points = get_line(source.pos,cir_pt)
    for line_pt in line_points[0::12]:
        for obs in obstacles:
            pygame.draw.circle(shadow_surf, WHITE, line_pt, 20, 0) #radius to 5px and 0 to fill the circle
            if obs.rect.collidepoint(line_pt) or pygame.Rect(0,0,500,500).collidepoint(line_pt) == False:
                return

光源、障害物、シャドウ マスクのマイ クラス:

class Obstacle(object):
    def __init__(self,x,y):
        self.surf = pygame.Surface((150,150))
        self.rect = pygame.Rect((x,y),(150,150))
        self.surf.fill(pygame.color.Color('blue'))

class Light_Source(object):
    def __init__(self,x,y,range_):
        self.range = range_
        self.pos = (x,y)


class Night_Mask(object):
    def __init__(self):
        self.surf = pygame.Surface((500,500)) #Screenwidth and height
        self.alpha = 100
        self.light_sources = []

        '''setting initial alpha and colorkey'''
        self.surf.set_colorkey(WHITE)
        self.surf.set_alpha(self.alpha)


    def apply_shadows(self, obstacles):
        shadow_surf = pygame.Surface((500,500))
        for source in self.light_sources:
            circle_pts = list(get_circle(source.pos,source.range))
            for cir_pt in circle_pts[0::3]:
                shadow_gen(shadow_surf,source,cir_pt,obstacles)
        self.surf.blit(shadow_surf, (0, 0))

Night_Mask クラスの apply_shadows メソッドで例外を使用せずに、ラインと障害物ループの両方から抜け出すことを可能にする影生成関数:

def shadow_gen(shadow_surf,source,cir_pt,obstacles):
    line_points = get_line(source.pos,cir_pt)
    for line_pt in line_points[0::12]:
        for obs in obstacles:
            pygame.draw.circle(shadow_surf, WHITE, line_pt, 20, 0) #radius to 5px and 0 to fill the circle
            if obs.rect.collidepoint(line_pt) or pygame.Rect(0,0,500,500).collidepoint(line_pt) == False:
                return

最後に、上記のすべてを実行するメインの pygame サンプル ループ:

pygame.init()
screen = pygame.display.set_mode((500, 500))

bg = pygame.Surface((500,500))
bg.fill(pygame.color.Color('yellow'))

ob_a = Obstacle(75,80)
ls = Light_Source(75,75,300)
night_m = Night_Mask()
night_m.light_sources.extend([ls])

while True:  
    screen.fill(pygame.color.Color('black'))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    ls.pos = pygame.mouse.get_pos()

    night_m.apply_shadows([ob_a])

    screen.blit(bg, (0,  0))
    screen.blit(ob_a.surf,ob_a.rect)
    screen.blit(night_m.surf, (0, 0))

    pygame.display.flip()

簡単にコピーして貼り付けるために、最初から最後までのコード全体を次に示します。

import pygame
import sys

WHITE = (255,255,255)
'''FUNCTIONS'''
def get_line(start, end):
    """Bresenham's Line Algorithm
    Produces a list of tuples from start and end

    >>> points1 = get_line((0, 0), (3, 4))
    >>> points2 = get_line((3, 4), (0, 0))
    >>> assert(set(points1) == set(points2))
    >>> print points1
    [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
    >>> print points2
    [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
    """
    # Setup initial conditions
    x1, y1 = start
    x2, y2 = end
    dx = x2 - x1
    dy = y2 - y1

    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)

    # Rotate line
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2

    # Swap start and end points if necessary and store swap state
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True

    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1

    # Calculate error
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1

    # Iterate over bounding box generating points between start and end
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx

    # Reverse the list if the coordinates were swapped
    if swapped:
        points.reverse()
    return points

def get_circle((dx,dy),radius):
    "Bresenham complete circle algorithm in Python"
    # init vars
    switch = 3 - (2 * radius)
    points = set()
    x = 0
    y = radius
    # first quarter/octant starts clockwise at 12 o'clock
    while x <= y:
        # first quarter first octant
        points.add((x,-y))
        # first quarter 2nd octant
        points.add((y,-x))
        # second quarter 3rd octant
        points.add((y,x))
        # second quarter 4.octant
        points.add((x,y))
        # third quarter 5.octant
        points.add((-x,y))        
        # third quarter 6.octant
        points.add((-y,x))
        # fourth quarter 7.octant
        points.add((-y,-x))
        # fourth quarter 8.octant
        points.add((-x,-y))
        if switch < 0:
            switch = switch + (4 * x) + 6
        else:
            switch = switch + (4 * (x - y)) + 10
            y = y - 1
        x = x + 1
    offset_points = set()
    for pt in points:
        offset_points.add((pt[0]+dx,pt[1]+dy))

    return offset_points

def shadow_gen(shadow_surf,source,cir_pt,obstacles):
    line_points = get_line(source.pos,cir_pt)
    for line_pt in line_points[0::12]:
        for obs in obstacles:
            pygame.draw.circle(shadow_surf, WHITE, line_pt, 20, 0) #radius to 5px and 0 to fill the circle
            if obs.rect.collidepoint(line_pt) or pygame.Rect(0,0,500,500).collidepoint(line_pt) == False:
                return

'''CLASSES'''                
class Obstacle(object):
    def __init__(self,x,y):
        self.surf = pygame.Surface((150,150))
        self.rect = pygame.Rect((x,y),(150,150))
        self.surf.fill(pygame.color.Color('blue'))

class Light_Source(object):
    def __init__(self,x,y,range_):
        self.range = range_
        self.pos = (x,y)


class Night_Mask(object):
    def __init__(self):
        self.surf = pygame.Surface((500,500)) #Screenwidth and height
        self.alpha = 100
        self.light_sources = []


        '''setting initial alpha and colorkey'''
        self.surf.set_colorkey(WHITE)
        self.surf.set_alpha(self.alpha)


    def apply_shadows(self, obstacles):
        shadow_surf = pygame.Surface((500,500))
        for source in self.light_sources:
            circle_pts = list(get_circle(source.pos,source.range))
            for cir_pt in circle_pts[0::3]:
                shadow_gen(shadow_surf,source,cir_pt,obstacles)
        self.surf.blit(shadow_surf, (0, 0))


'''MAIN GAME'''
pygame.init()
screen = pygame.display.set_mode((500, 500))

bg = pygame.Surface((500,500))
bg.fill(pygame.color.Color('yellow'))

ob_a = Obstacle(75,80)
ls = Light_Source(75,75,300)
night_m = Night_Mask()
night_m.light_sources.extend([ls])

while True:  
    screen.fill(pygame.color.Color('black'))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    ls.pos = pygame.mouse.get_pos()

    night_m.apply_shadows([ob_a])

    screen.blit(bg, (0,  0))
    screen.blit(ob_a.surf,ob_a.rect)
    screen.blit(night_m.surf, (0, 0))

    pygame.display.flip()
4

1 に答える 1

1

あなたのラグの問題は method から来ているようNight_Mask.apply_shadows(self, obstacles)です。これは、ネストされた for ループが通過する必要がある反復の純粋な量によるものと思われます。

range_のコンストラクターでの値をLight_Source(x, y, range_)減らすと、前述のメソッドの繰り返しが減りラグが減りますが、視覚効果は悪くなります。変数を 65 ~ 70 を超えて設定した後、fps が実際に低下し始めたことがわかりました。

影を非常にうまく処理する Pygame グラフィックス ライブラリがあります。

ページへのリンク: http://pygame.org/project-Pygame+Advanced+Graphics+Library-660-4586.html サイトからバージョン 8.1.1 を直接ダウンロード:リンク

これは、サイトからのライブラリの説明です。

これは、最小限のコードで複雑な効果をすばやく簡単に作成するための多目的グラフィック ライブラリです。それぞれ 1 ページ未満の長さ (コメントは数えません) の非常によくコメントされた例を実行し、影やアンチエイリアスなどの複雑な効果を作成する方法を学びます。

これは、影の例を示すページの画像です。 ここに画像の説明を入力

ライブラリをダウンロードしてテストしましたが、非常にうまく機能します。Python 3.4のPygame1.9.2a0でテストしました

これがあなたの問題の最も簡単な修正であり、将来のプロジェクトにも役立つはずです. これが役立つことを願っています。

于 2016-06-20T07:21:56.023 に答える