首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在ursina python中增加FPS

如何在ursina python中增加FPS
EN

Stack Overflow用户
提问于 2021-10-02 02:09:57
回答 1查看 558关注 0票数 0

我想创建生存游戏与无限块地形(如我的世界)。所以我使用ursina python游戏引擎,你可以看到它的here

因此,我使用perlin噪声来创建带有内置ursina块模型的地形。我测试了前25个区块,它在100FPS以上的情况下工作得很好,所以我开始增加到250个区块,因为我想要一个无限的地形。但我遇到了一些问题,当我增加到100块或更多时,我的FPS开始降低到30 FPS以下(我只创建了一个层)。

下面是我的代码:

代码语言:javascript
运行
复制
#-------------------------------Noise.py(I got on the github)-------------------------
# Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com)
# see LICENSE.txt for details

"""Perlin noise -- pure python implementation"""

__version__ = '$Id: perlin.py 521 2008-12-15 03:03:52Z casey.duncan $'

from math import floor, fmod, sqrt
from random import randint

# 3D Gradient vectors
_GRAD3 = ((1,1,0),(-1,1,0),(1,-1,0),(-1,-1,0), 
    (1,0,1),(-1,0,1),(1,0,-1),(-1,0,-1), 
    (0,1,1),(0,-1,1),(0,1,-1),(0,-1,-1),
    (1,1,0),(0,-1,1),(-1,1,0),(0,-1,-1),
) 

# 4D Gradient vectors
_GRAD4 = ((0,1,1,1), (0,1,1,-1), (0,1,-1,1), (0,1,-1,-1), 
    (0,-1,1,1), (0,-1,1,-1), (0,-1,-1,1), (0,-1,-1,-1), 
    (1,0,1,1), (1,0,1,-1), (1,0,-1,1), (1,0,-1,-1), 
    (-1,0,1,1), (-1,0,1,-1), (-1,0,-1,1), (-1,0,-1,-1), 
    (1,1,0,1), (1,1,0,-1), (1,-1,0,1), (1,-1,0,-1), 
    (-1,1,0,1), (-1,1,0,-1), (-1,-1,0,1), (-1,-1,0,-1), 
    (1,1,1,0), (1,1,-1,0), (1,-1,1,0), (1,-1,-1,0), 
    (-1,1,1,0), (-1,1,-1,0), (-1,-1,1,0), (-1,-1,-1,0))

# A lookup table to traverse the simplex around a given point in 4D. 
# Details can be found where this table is used, in the 4D noise method. 
_SIMPLEX = (
    (0,1,2,3),(0,1,3,2),(0,0,0,0),(0,2,3,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,2,3,0), 
    (0,2,1,3),(0,0,0,0),(0,3,1,2),(0,3,2,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,3,2,0), 
    (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), 
    (1,2,0,3),(0,0,0,0),(1,3,0,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,3,0,1),(2,3,1,0), 
    (1,0,2,3),(1,0,3,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,0,3,1),(0,0,0,0),(2,1,3,0), 
    (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), 
    (2,0,1,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,0,1,2),(3,0,2,1),(0,0,0,0),(3,1,2,0), 
    (2,1,0,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,1,0,2),(0,0,0,0),(3,2,0,1),(3,2,1,0))

# Simplex skew constants
_F2 = 0.5 * (sqrt(3.0) - 1.0)
_G2 = (3.0 - sqrt(3.0)) / 6.0
_F3 = 1.0 / 3.0
_G3 = 1.0 / 6.0


class BaseNoise:
    """Noise abstract base class"""

    permutation = (151,160,137,91,90,15, 
        131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 
        190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 
        88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166, 
        77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 
        102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196, 
        135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123, 
        5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 
        223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9, 
        129,22,39,253,9,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228, 
        251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 
        49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254, 
        138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180)

    period = len(permutation)

    # Double permutation array so we don't need to wrap
    permutation = permutation * 2

    randint_function = randint

    def __init__(self, period=None, permutation_table=None, randint_function=None):
        """Initialize the noise generator. With no arguments, the default
        period and permutation table are used (256). The default permutation
        table generates the exact same noise pattern each time.
        
        An integer period can be specified, to generate a random permutation
        table with period elements. The period determines the (integer)
        interval that the noise repeats, which is useful for creating tiled
        textures.  period should be a power-of-two, though this is not
        enforced. Note that the speed of the noise algorithm is indpendent of
        the period size, though larger periods mean a larger table, which
        consume more memory.
        A permutation table consisting of an iterable sequence of whole
        numbers can be specified directly. This should have a power-of-two
        length. Typical permutation tables are a sequnce of unique integers in
        the range [0,period) in random order, though other arrangements could
        prove useful, they will not be "pure" simplex noise. The largest
        element in the sequence must be no larger than period-1.
        period and permutation_table may not be specified together.
        A substitute for the method random.randint(a, b) can be chosen. The
        method must take two integer parameters a and b and return an integer N
        such that a <= N <= b.
        """
        if randint_function is not None:  # do this before calling randomize()
            if not hasattr(randint_function, '__call__'):
                raise TypeError(
                    'randint_function has to be a function')
            self.randint_function = randint_function
            if period is None:
                period = self.period  # enforce actually calling randomize()
        if period is not None and permutation_table is not None:
            raise ValueError(
                'Can specify either period or permutation_table, not both')
        if period is not None:
            self.randomize(period)
        elif permutation_table is not None:
            self.permutation = tuple(permutation_table) * 2
            self.period = len(permutation_table)

    def randomize(self, period=None):
        """Randomize the permutation table used by the noise functions. This
        makes them generate a different noise pattern for the same inputs.
        """
        if period is not None:
            self.period = period
        perm = list(range(self.period))
        perm_right = self.period - 1
        for i in list(perm):
            j = self.randint_function(0, perm_right)
            perm[i], perm[j] = perm[j], perm[i]
        self.permutation = tuple(perm) * 2


class SimplexNoise(BaseNoise):
    """Perlin simplex noise generator
    Adapted from Stefan Gustavson's Java implementation described here:
    http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
    To summarize:
    "In 2001, Ken Perlin presented 'simplex noise', a replacement for his classic
    noise algorithm.  Classic 'Perlin noise' won him an academy award and has
    become an ubiquitous procedural primitive for computer graphics over the
    years, but in hindsight it has quite a few limitations.  Ken Perlin himself
    designed simplex noise specifically to overcome those limitations, and he
    spent a lot of good thinking on it. Therefore, it is a better idea than his
    original algorithm. A few of the more prominent advantages are: 
    * Simplex noise has a lower computational complexity and requires fewer
      multiplications. 
    * Simplex noise scales to higher dimensions (4D, 5D and up) with much less
      computational cost, the complexity is O(N) for N dimensions instead of 
      the O(2^N) of classic Noise. 
    * Simplex noise has no noticeable directional artifacts.  Simplex noise has 
      a well-defined and continuous gradient everywhere that can be computed 
      quite cheaply. 
    * Simplex noise is easy to implement in hardware."
    """

    def noise2(self, x, y):
        """2D Perlin simplex noise. 
        
        Return a floating point value from -1 to 1 for the given x, y coordinate. 
        The same value is always returned for a given x, y pair unless the
        permutation table changes (see randomize above). 
        """
        # Skew input space to determine which simplex (triangle) we are in
        s = (x + y) * _F2
        i = floor(x + s)
        j = floor(y + s)
        t = (i + j) * _G2
        x0 = x - (i - t) # "Unskewed" distances from cell origin
        y0 = y - (j - t)

        if x0 > y0:
            i1 = 1; j1 = 0 # Lower triangle, XY order: (0,0)->(1,0)->(1,1)
        else:
            i1 = 0; j1 = 1 # Upper triangle, YX order: (0,0)->(0,1)->(1,1)
        
        x1 = x0 - i1 + _G2 # Offsets for middle corner in (x,y) unskewed coords
        y1 = y0 - j1 + _G2
        x2 = x0 + _G2 * 2.0 - 1.0 # Offsets for last corner in (x,y) unskewed coords
        y2 = y0 + _G2 * 2.0 - 1.0

        # Determine hashed gradient indices of the three simplex corners
        perm = self.permutation
        ii = int(i) % self.period
        jj = int(j) % self.period
        gi0 = perm[ii + perm[jj]] % 12
        gi1 = perm[ii + i1 + perm[jj + j1]] % 12
        gi2 = perm[ii + 1 + perm[jj + 1]] % 12

        # Calculate the contribution from the three corners
        tt = 0.5 - x0**2 - y0**2
        if tt > 0:
            g = _GRAD3[gi0]
            noise = tt**4 * (g[0] * x0 + g[1] * y0)
        else:
            noise = 0.0
        
        tt = 0.5 - x1**2 - y1**2
        if tt > 0:
            g = _GRAD3[gi1]
            noise += tt**4 * (g[0] * x1 + g[1] * y1)
        
        tt = 0.5 - x2**2 - y2**2
        if tt > 0:
            g = _GRAD3[gi2]
            noise += tt**4 * (g[0] * x2 + g[1] * y2)

        return noise * 70.0 # scale noise to [-1, 1]

    def noise3(self, x, y, z):
        """3D Perlin simplex noise. 
        
        Return a floating point value from -1 to 1 for the given x, y, z coordinate. 
        The same value is always returned for a given x, y, z pair unless the
        permutation table changes (see randomize above).
        """
        # Skew the input space to determine which simplex cell we're in
        s = (x + y + z) * _F3
        i = floor(x + s)
        j = floor(y + s)
        k = floor(z + s)
        t = (i + j + k) * _G3
        x0 = x - (i - t) # "Unskewed" distances from cell origin
        y0 = y - (j - t)
        z0 = z - (k - t)

        # For the 3D case, the simplex shape is a slightly irregular tetrahedron. 
        # Determine which simplex we are in. 
        if x0 >= y0:
            if y0 >= z0:
                i1 = 1; j1 = 0; k1 = 0
                i2 = 1; j2 = 1; k2 = 0
            elif x0 >= z0:
                i1 = 1; j1 = 0; k1 = 0
                i2 = 1; j2 = 0; k2 = 1
            else:
                i1 = 0; j1 = 0; k1 = 1
                i2 = 1; j2 = 0; k2 = 1
        else: # x0 < y0
            if y0 < z0:
                i1 = 0; j1 = 0; k1 = 1
                i2 = 0; j2 = 1; k2 = 1
            elif x0 < z0:
                i1 = 0; j1 = 1; k1 = 0
                i2 = 0; j2 = 1; k2 = 1
            else:
                i1 = 0; j1 = 1; k1 = 0
                i2 = 1; j2 = 1; k2 = 0
        
        # Offsets for remaining corners
        x1 = x0 - i1 + _G3
        y1 = y0 - j1 + _G3
        z1 = z0 - k1 + _G3
        x2 = x0 - i2 + 2.0 * _G3
        y2 = y0 - j2 + 2.0 * _G3
        z2 = z0 - k2 + 2.0 * _G3
        x3 = x0 - 1.0 + 3.0 * _G3
        y3 = y0 - 1.0 + 3.0 * _G3
        z3 = z0 - 1.0 + 3.0 * _G3

        # Calculate the hashed gradient indices of the four simplex corners
        perm = self.permutation
        ii = int(i) % self.period
        jj = int(j) % self.period
        kk = int(k) % self.period
        gi0 = perm[ii + perm[jj + perm[kk]]] % 12
        gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12
        gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12
        gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12

        # Calculate the contribution from the four corners
        noise = 0.0
        tt = 0.6 - x0**2 - y0**2 - z0**2
        if tt > 0:
            g = _GRAD3[gi0]
            noise = tt**4 * (g[0] * x0 + g[1] * y0 + g[2] * z0)
        else:
            noise = 0.0
        
        tt = 0.6 - x1**2 - y1**2 - z1**2
        if tt > 0:
            g = _GRAD3[gi1]
            noise += tt**4 * (g[0] * x1 + g[1] * y1 + g[2] * z1)
        
        tt = 0.6 - x2**2 - y2**2 - z2**2
        if tt > 0:
            g = _GRAD3[gi2]
            noise += tt**4 * (g[0] * x2 + g[1] * y2 + g[2] * z2)
        
        tt = 0.6 - x3**2 - y3**2 - z3**2
        if tt > 0:
            g = _GRAD3[gi3]
            noise += tt**4 * (g[0] * x3 + g[1] * y3 + g[2] * z3)
        
        return noise * 32.0


def lerp(t, a, b):
    return a + t * (b - a)

def grad3(hash, x, y, z):
    g = _GRAD3[hash % 16]
    return x*g[0] + y*g[1] + z*g[2]


class TileableNoise(BaseNoise):
    """Tileable implemention of Perlin "improved" noise. This
    is based on the reference implementation published here:
    
    http://mrl.nyu.edu/~perlin/noise/
    """

    def noise3(self, x, y, z, repeat, base=0.0):
        """Tileable 3D noise.
        
        repeat specifies the integer interval in each dimension 
        when the noise pattern repeats.
        
        base allows a different texture to be generated for
        the same repeat interval.
        """
        i = int(fmod(floor(x), repeat))
        j = int(fmod(floor(y), repeat))
        k = int(fmod(floor(z), repeat))
        ii = (i + 1) % repeat
        jj = (j + 1) % repeat
        kk = (k + 1) % repeat
        if base:
            i += base; j += base; k += base
            ii += base; jj += base; kk += base

        x -= floor(x); y -= floor(y); z -= floor(z)
        fx = x**3 * (x * (x * 6 - 15) + 10)
        fy = y**3 * (y * (y * 6 - 15) + 10)
        fz = z**3 * (z * (z * 6 - 15) + 10)

        perm = self.permutation
        A = perm[i]
        AA = perm[A + j]
        AB = perm[A + jj]
        B = perm[ii]
        BA = perm[B + j]
        BB = perm[B + jj]
        
        return lerp(fz, lerp(fy, lerp(fx, grad3(perm[AA + k], x, y, z),
                                          grad3(perm[BA + k], x - 1, y, z)),
                                 lerp(fx, grad3(perm[AB + k], x, y - 1, z),
                                          grad3(perm[BB + k], x - 1, y - 1, z))),
                        lerp(fy, lerp(fx, grad3(perm[AA + kk], x, y, z - 1),
                                          grad3(perm[BA + kk], x - 1, y, z - 1)),
                                 lerp(fx, grad3(perm[AB + kk], x, y - 1, z - 1),
                                          grad3(perm[BB + kk], x - 1, y - 1, z - 1))))
#--------------------------Math.py(For InverseLefp)--------------------------------
def Clamp(t: float, minimum: float, maximum: float):
    """Float result between a min and max values."""
    value = t
    if t < minimum:
        value = minimum
    elif t > maximum:
        value = maximum
    return value
def InverseLefp(a: float, b: float, value: float):
    if a != b:
        return Clamp((value - a) / (b - a), 0, 1)
    return 0  
#-----------------------------Game.py(Main code)----------------------
from ursina import *
from ursina.prefabs import *
from ursina.prefabs.first_person_controller import *
from Math import InverseLefp
import Noise
app = Ursina()

#The maximum height of the terrain
maxHeight = 10
#Control the width and height of the map
mapWidth = 10
mapHeight = 10
#A class that create a block
class Voxel(Button):
    def __init__(self, position=(0,0,0)):
        super().__init__(
            parent = scene,
            position = position,
            model = 'cube',
            origin_y = .5,
            texture = 'white_cube',
            color = color.color(0, 0, random.uniform(.9, 1.0)),
            highlight_color = color.lime,
        )

    #Detect user key input
    def input(self, key):
        if self.hovered:
            if key == 'right mouse down':
                #Place block if user right click
                voxel = Voxel(position=self.position + mouse.normal)
            if key == 'left mouse down':
                #Break block if user left click
                destroy(self)
            if key == 'escape':
                #Exit the game if user press the esc key
                app.userExit()
#Return perlin noise value between 0 and 1 with x, y position with scale = noiseScale
def GeneratedNoiseMap(y: int, x: int, noiseScale: float):
    #Check if the noise scale was invalid or not
    if noiseScale <= 0:
        noiseScale = 0.001
    sampleX = x / noiseScale
    sampleY = y / noiseScale
    #The Noise.SimplexNoise().noise2 will return the value between -1 and 1
    perlinValue = Noise.SimplexNoise().noise2(sampleX, sampleY)   
    #The InverseLefp will make the value scale to between 0 and 1
    perlinValue = InverseLefp(-1, 1, perlinValue)
    return perlinValue

for z in range(mapHeight):
    for x in range(mapWidth):
        #Calculating the height of the block and round it to integer
        height = round(GeneratedNoiseMap(z, x, 20) * maxHeight)
        #Place the block and make it always below the player
        block = Voxel(position=(x, height - maxHeight - 1, z))
        #Set the collider of the block
        block.collider = 'mesh'

#Character movement
player = FirstPersonController()
#Run the game
app.run()

所有文件都在同一个文件夹中。

它工作得很好,但是FPS非常低,所以有人能帮上忙吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-05 05:23:02

我现在还不能测试这段代码,但这可以作为一个起点:

代码语言:javascript
运行
复制
level_parent = Entity(model=Mesh(vertices=[], uvs=[]))

for z in range(mapHeight):
    for x in range(mapWidth):
        height = round(GeneratedNoiseMap(z, x, 20) * maxHeight)
        block = Voxel(position=(x, height - maxHeight - 1, z))
        level_parent.model.vertices.extend(block.model.vertices)
        
level_parent.collider = 'mesh' # call this only once after all vertices are set up

对于纹理,可能还需要将每个块中的block.uvs添加到level_parent.model.uvs中。或者,在设置顶点后调用level_parent.model.project_uvs()

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69413473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档