我该如何以这种方式管理类,以便根据需要创建该类的任意数量的对象,同时不造成数据泄露?

编程语言 2026-07-12

我已经在一个项目上工作了一段时间,最近在尝试为我正在开发的游戏5加载一个“谱面”时,遇到了一个问题:游戏会无响应,吞吃全部内存(RAM),直到崩溃。我现在猜测这是代码中的数据泄漏。我知道问题会出现在这三个脚本中的一个(或多个),因为它们是唯一在管理我的音符的脚本。

总体而言,我的目标是在屏幕上同时生成固定数量的音符,能够在同一时间更新它们,并且如果它们移出屏幕就能够删除它们。

我承认我的命名风格有些不一致,不过就这个项目的性质而言,这不应该造成太大问题。

https://github.com/StrategicVirus/RhythmGameMayhem-Love2d

game.lua

require("load_level")

game={}
scale=0.0520833333
current_max = 30
chart_time_start = love.timer.getTime()

function game.load()
    local r, g, b = love.math.colorFromBytes(0x00, 0x00, 0x00)
    love.graphics.setBackgroundColor(r, g, b)
    if love.filesystem.getInfo("RGM") == nil then
        love.filesystem.createDirectory("RGM")
    end
    note_image = love.graphics.newImage("img/Note_1.png")
    --next line is used for testing purposes on chart loading, commented out for screen tests. 
    load_chart("173612 xi - FREEDOM DiVE","xi - FREEDOM DiVE (razlteh) [4K Another]", 7)
    --load_chart("Testing_script", "Multiple_notes_test", 0)


end

function game.update(dt)
    notesClass:update()
end

function game.draw()
    love.graphics.setColor(1,1,1)
    love.graphics.rectangle("line", 700, 0, 500, 1280) -- 700 to 1200
    love.graphics.rectangle("line", 720, 20, 100, 1240) -- 720 to 820
    love.graphics.rectangle("line", 840, 20, 100, 1240) -- 840 to 940
    love.graphics.rectangle("line", 960, 20, 100, 1240) -- 960 to 1060
    love.graphics.rectangle("line", 1080, 20, 100, 1240) -- 1080 to 1180
    notesClass:draw()
end

function game.keypressed(key)
    if (key == "d") then
        print("delete column 1")
        current_max = current_max+ 1
    elseif (key == "f") then
        print("delete column 2")
        current_max = current_max+ 1
    elseif (key == "j") then
        print("delete column 3")
        current_max = current_max+ 1
    elseif (key == "k") then
        print("delete column 4") 
        current_max = current_max+ 1
    end
end

load_level.lua

notesClass = require ("notesClass")

note_table={id=0, x=0, hitTime=0, speed=0, column=0}

-- used to create tokens from each of the lines. Will be used in several instances through load_level and new_note
function tokeniser(s, split)
    split = split or '%s'
    local t={}
    local i=1
    for str in string.gmatch(s, '([^'..split..']+)') do 
        t[i] = str
        i=i+1
    end
    return t
end

function find_info(r)
    --r = tonumber(r)
    if r == 1 then
        return ("MIN")
    elseif r == 7 then 
        return ("SONG") 
    elseif r==0 then
        return("TEST")
    end
end

function load_chart(BMP_ID, DIF_NAME, REQ_INFO)
    NAME=DIF_NAME ..".osu"
    ID=BMP_ID.."/"
    local HitObjects, TimingPoints, Difficulty, Editor, General, speed, iterator = false, false, false, false, false, 1, 1
    REQUIRED = find_info(REQ_INFO)
    for line in love.filesystem.lines(ID..NAME) do
        if (line == "[HitObjects]" or HitObjects) and REQUIRED ~= "MIN" or REQUIRED == "TEST" then
            TimingPoints=false
            Metadata = false
            if HitObjects then 
                local cL=tokeniser(line, ",")
                for i=1, 5, 1 do
                    cL[i] = tonumber(cL[i])
                end
                cL[6] = nil
                local new_note = notesClass:new(iterator, cL[1], cL[3], speed)
                table.insert(note_table, new_note)
                iterator = iterator+1
            end
            HitObjects = true
        else if (line == "[Metadata]" or Metadata) and REQUIRED ~= "SONG" then
            General = false
            if Metadata then
                local cL=tokeniser(line, ":")
                if cL[1] == "Title" then
                    t = cL[2]
                elseif cL[1] == "Version" then
                    v = cL[2]
                end
                if t ~= nil and v ~= nil then
                    local song_data = {title=t, version=v}
                    return song_data
                end 
            end
            Metadata = true
        else if (line == "[General]" or General) and REQUIRED ~= "MIN" then
            if General then
                local cL = tokeniser(line, ":")
                if cL[1] == "AudioFilename" then
                    c = string.sub(cL[2], 2)
                    source = love.audio.newSource(ID..c, "stream")
                    source:play()
                end
            end
        end
    end
end
end
end

notesClass.lua

notesClass={id=0, x=0, hitTime=0, speed=0, column=0}
notesClassMT = {__index = notesClass}

local function getColumn(c)
    if c == 64 then
        return 1
    elseif c == 192 then
        return 2
    elseif c == 320 then
        return 3
    elseif c == 448 then
        return 4
    else
       -- print("this is not a 4K map. Error")
    end
end

function notesClass:new(id, x, hitTime, speed)
    local note=setmetatable({id=0, x=0, hitTime=0, speed=0, column=0},  notesClassMT)
    x=tonumber(x)
    note.id=id or 0
    note.x=x or 0
    note.hitTime=tonumber(hitTime) or 0
    note.speed=tonumber(speed) or 0
    note.image = love.graphics.newImage("img/Note_1.png")
    note.off_screen = true
    note.updated = false
    return note
end

function notesClass:load() 
end

function notesClass:update(dt)
    if notesClass.id <= current_max then
        notesClass.hitTime = notesClass.hitTime +( 1 * 5 )

        if notesClass.hitTime > 1080 then
            notesClass.off_screen = true
        end

        if notesClass.updated == false then
            update_time = love.timer.getTime() - chart_time_start 
            notesClass.hitTime = notesClass.hitTime + update_time
            notesClass.updated = true
            notesClass.off_screen = false 
        end
    end
    print(notesClass.hitTime)
end
function notesClass:draw()
    if notesClass.off_screen == false then
        love.graphics.draw(notesClass.image, notesClass.x, notesClass.hitTime, 0, scale, scale)
    end
end

return notesClass

解决方案

因为每次你创建一个新的音符时,也会创建一个新的 Image 对象:

function notesClass:new(id, x, hitTime, speed)
    ...
    note.image = love.graphics.newImage("img/Note_1.png")
    ...
end

但你似乎知道图片应该在load函数中加载:

function game.load()
    ...
    note_image = love.graphics.newImage("img/Note_1.png")
    ...
end

你需要做的是重用这个对象,因为你已经将 notesClass 自身设置为每个音符对象的元表,你可以直接这样做:

function game.load()
    ...
    notesClass.image = love.graphics.newImage("img/Note_1.png")
    ...
end

function notesClass:new(id, x, hitTime, speed)
    ...
    --note.image = love.graphics.newImage("img/Note_1.png")
    ...
end
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章