如何在Lua上为乒乓球游戏添加边界,使球从黑线上弹跳



我正试图在我的乒乓游戏周围得到一个边界,同时仍然能够正常玩游戏。我不知道该怎么做。有人能帮帮我吗?

我的代码

主要


push = require 'push'

Class = require 'class'

require 'Paddle'

require 'Ball'
function love.draw()
love.graphics.rectangle("fill", 0, 0, 0, 255 )
end
-- size of our actual window
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
-- size we're trying to emulate with push
VIRTUAL_WIDTH = 432
VIRTUAL_HEIGHT = 243
-- paddle movement speed
PADDLE_SPEED = 235
--[[
Called just once at the beginning of the game; used to set up
game objects, variables, etc. and prepare the game world.
]]
function love.load()
-- set love's default filter to "nearest-neighbor", which essentially
-- means there will be no filtering of pixels (blurriness), which is
-- important for a nice crisp, 2D look
love.graphics.setDefaultFilter('nearest', 'nearest')
-- set the title of our application window
love.window.setTitle('Pong')
-- seed the RNG so that calls to random are always random
math.randomseed(os.time())
-- initialize our nice-looking retro text fonts
smallFont = love.graphics.newFont('font.ttf', 8)
largeFont = love.graphics.newFont('font.ttf', 16)
scoreFont = love.graphics.newFont('font.ttf', 32)
love.graphics.setFont(smallFont)
-- set up our sound effects; later, we can just index this table and
-- call each entry's `play` method
sounds = {
['paddle_hit'] = love.audio.newSource('sounds/paddle_hit.wav', 'static'),
['score'] = love.audio.newSource('sounds/score.wav', 'static'),
['wall_hit'] = love.audio.newSource('sounds/wall_hit.wav', 'static')
}

-- initialize our virtual resolution, which will be rendered within our
-- actual window no matter its dimensions
push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, {
fullscreen = false,
resizable = true,
vsync = true,
canvas = false
})
-- initialize our player paddles; make them global so that they can be
-- detected by other functions and modules
player1 = Paddle(10, 30, 5, 20)
player2 = Paddle(VIRTUAL_WIDTH - 10, VIRTUAL_HEIGHT - 30, 5, 20)
-- place a ball in the middle of the screen
ball = Ball(VIRTUAL_WIDTH / 2 - 2, VIRTUAL_HEIGHT / 2 - 2, 4, 4)
-- initialize score variables
player1Score = 0
player2Score = 0
-- either going to be 1 or 2; whomever is scored on gets to serve the
-- following turn
servingPlayer = 1
-- player who won the game; not set to a proper value until we reach
-- that state in the game
winningPlayer = 0
gameState = 'start'
end

function love.resize(w, h)
push:resize(w, h)
end

function love.update(dt)
if gameState == 'serve' then

ball.dy = math.random(-50, 50)
if servingPlayer == 1 then
ball.dx = math.random(140, 200)
else
ball.dx = -math.random(140, 200)
end
elseif gameState == 'play' then
if ball:collides(player1) then
ball.dx = -ball.dx * 1.03
ball.x = player1.x + 5
-- keep velocity going in the same direction, but randomize it
if ball.dy < 0 then
ball.dy = -math.random(10, 150)
else
ball.dy = math.random(10, 150)
end
sounds['paddle_hit']:play()
end
if ball:collides(player2) then
ball.dx = -ball.dx * 1.03
ball.x = player2.x - 4
-- keep velocity going in the same direction, but randomize it
if ball.dy < 0 then
ball.dy = -math.random(10, 150)
else
ball.dy = math.random(10, 150)
end
sounds['paddle_hit']:play()
end
-- detect upper and lower screen boundary collision, playing a sound
-- effect and reversing dy if true
if ball.y <= 0 then
ball.y = 0
ball.dy = -ball.dy
sounds['wall_hit']:play()
end
-- -4 to account for the ball's size
if ball.y >= VIRTUAL_HEIGHT - 4 then
ball.y = VIRTUAL_HEIGHT - 4
ball.dy = -ball.dy
sounds['wall_hit']:play()
end
-- if we reach the left or right edge of the screen, go back to serve
-- and update the score and serving player
if ball.x < 0 then
servingPlayer = 1
player2Score = player2Score + 1
sounds['score']:play()
-- if we've reached a score of 10, the game is over; set the
-- state to done so we can show the victory message
if player2Score == 10 then
winningPlayer = 2
gameState = 'done'
else
gameState = 'serve'
-- places the ball in the middle of the screen, no velocity
ball:reset()
end
end
if ball.x > VIRTUAL_WIDTH then
servingPlayer = 2
player1Score = player1Score + 1
sounds['score']:play()
if player1Score == 10 then
winningPlayer = 1
gameState = 'done'
else
gameState = 'serve'
ball:reset()
end
end
end

if love.keyboard.isDown('w') then
player1.dy = -PADDLE_SPEED
elseif love.keyboard.isDown('s') then
player1.dy = PADDLE_SPEED
else
player1.dy = 0
end
-- player 2
if love.keyboard.isDown('up') then
player2.dy = -PADDLE_SPEED
elseif love.keyboard.isDown('down') then
player2.dy = PADDLE_SPEED
else
player2.dy = 0
end

if gameState == 'play' then
ball:update(dt)
end
player1:update(dt)
player2:update(dt)
end

function love.keypressed(key)
-- `key` will be whatever key this callback detected as pressed
if key == 'escape' then
-- the function LÖVE2D uses to quit the application
love.event.quit()
-- if we press enter during either the start or serve phase, it should
-- transition to the next appropriate state
elseif key == 'enter' or key == 'return' then
if gameState == 'start' then
gameState = 'serve'
elseif gameState == 'serve' then
gameState = 'play'
elseif gameState == 'done' then
-- game is simply in a restart phase here, but will set the serving
-- player to the opponent of whomever won for fairness!
gameState = 'serve'
ball:reset()
-- reset scores to 0
player1Score = 0
player2Score = 0
-- decide serving player as the opposite of who won
if winningPlayer == 1 then
servingPlayer = 2
else
servingPlayer = 1
end
end
end
end
--[[
Called each frame after update; is responsible simply for
drawing all of our game objects and more to the screen.
]]
function love.draw()
-- begin drawing with push, in our virtual resolution
push:start()
love.graphics.clear(255/255, 255/255, 255/255, 255/255)

-- render different things depending on which part of the game we're in
if gameState == 'start' then
-- UI messages
love.graphics.setFont(smallFont)
love.graphics.printf('Welcome to Pong!', 0, 10, VIRTUAL_WIDTH, 'center')
love.graphics.printf('Press Enter to begin!', 0, 20, VIRTUAL_WIDTH, 'center')
elseif gameState == 'serve' then
-- UI messages
love.graphics.setFont(smallFont)
love.graphics.printf('Player ' .. tostring(servingPlayer) .. "'s serve!", 
0, 10, VIRTUAL_WIDTH, 'center')
love.graphics.printf('Press Enter to serve!', 0, 20, VIRTUAL_WIDTH, 'center')
elseif gameState == 'play' then
-- no UI messages to display in play
elseif gameState == 'done' then
-- UI messages
love.graphics.setFont(largeFont)
love.graphics.printf('Player ' .. tostring(winningPlayer) .. ' wins!',
0, 10, VIRTUAL_WIDTH, 'center')
love.graphics.setFont(smallFont)
love.graphics.printf('Press Enter to restart!', 0, 30, VIRTUAL_WIDTH, 'center')
end
-- show the score before ball is rendered so it can move over the text
displayScore()

player1:render()
player2:render()
ball:render()
-- display FPS for debugging; simply comment out to remove
displayFPS()
-- end our drawing to push
push:finish()
end
--[[
Simple function for rendering the scores.
]]
function displayScore()
-- score display
love.graphics.setFont(scoreFont)
love.graphics.print(tostring(player1Score), VIRTUAL_WIDTH / 2 - 50,
VIRTUAL_HEIGHT / 3)
love.graphics.print(tostring(player2Score), VIRTUAL_WIDTH / 2 + 30,
VIRTUAL_HEIGHT / 3)
end
--[[
Renders the current FPS.
]]
function displayFPS()
-- simple FPS display across all states
love.graphics.setFont(largeFont)
love.graphics.setColor(0, 0/255, 0, 0/255)
love.graphics.print('FPS: ' .. tostring(love.timer.getFPS()), 10, 10)
love.graphics.setColor(0, 0, 0, 255)
end


Ball = Class{}
function Ball:init(x, y, width, height)
self.x = x
self.y = y
self.width = width
self.height = height
-- these variables are for keeping track of our velocity on both the
-- X and Y axis, since the ball can move in two dimensions
self.dy = 0
self.dx = 0
end
--[[
Expects a paddle as an argument and returns true or false, depending
on whether their rectangles overlap.
]]
function Ball:collides(paddle)
-- first, check to see if the left edge of either is farther to the right
-- than the right edge of the other
if self.x > paddle.x + paddle.width or paddle.x > self.x + self.width then
return false
end
-- then check to see if the bottom edge of either is higher than the top
-- edge of the other
if self.y > paddle.y + paddle.height or paddle.y > self.y + self.height then
return false
end 
-- if the above aren't true, they're overlapping
return true
end
--[[
Places the ball in the middle of the screen, with no movement.
]]
function Ball:reset()
self.x = VIRTUAL_WIDTH / 2 - 2
self.y = VIRTUAL_HEIGHT / 2 - 2
self.dx = 0
self.dy = 0
end
function Ball:update(dt)
self.x = self.x + self.dx * dt
self.y = self.y + self.dy * dt
end
function Ball:render()
love.graphics.rectangle('fill', self.x, self.y, self.width, self.height)
end


Paddle = Class{}
function Paddle:init(x, y, width, height)
self.x = x
self.y = y
self.width = width
self.height = height
self.dy = 0
end
function Paddle:update(dt)
if self.dy < 0 then
self.y = math.max(0, self.y + self.dy * dt)
else
self.y = math.min(VIRTUAL_HEIGHT - self.height, self.y + self.dy * dt)
end
end

function Paddle:render()
love.graphics.rectangle('fill', self.x, self.y, self.width, self.height)
end


local love11 = love.getVersion() == 11
local getDPI = love11 and love.window.getDPIScale or love.window.getPixelScale
local windowUpdateMode = love11 and love.window.updateMode or function(width, height, settings)
local _, _, flags = love.window.getMode()
for k, v in pairs(settings) do flags[k] = v end
love.window.setMode(width, height, flags)
end
local push = {

defaults = {
fullscreen = false,
resizable = false,
pixelperfect = false,
highdpi = true,
canvas = true,
stencil = true
}

}
setmetatable(push, push)
function push:applySettings(settings)
for k, v in pairs(settings) do
self["_" .. k] = v
end
end
function push:resetSettings() return self:applySettings(self.defaults) end
function push:setupScreen(WWIDTH, WHEIGHT, RWIDTH, RHEIGHT, settings)
settings = settings or {}
self._WWIDTH, self._WHEIGHT = WWIDTH, WHEIGHT
self._RWIDTH, self._RHEIGHT = RWIDTH, RHEIGHT
self:applySettings(self.defaults) --set defaults first
self:applySettings(settings) --then fill with custom settings

windowUpdateMode(self._RWIDTH, self._RHEIGHT, {
fullscreen = self._fullscreen,
resizable = self._resizable,
highdpi = self._highdpi
})
self:initValues()
if self._canvas then
self:setupCanvas({ "default" }) --setup canvas
end
self._borderColor = {0, 0, 0}
self._drawFunctions = {
["start"] = self.start,
["end"] = self.finish
}
return self
end
function push:setupCanvas(canvases)
table.insert(canvases, { name = "_render", private = true }) --final render
self._canvas = true
self.canvases = {}
for i = 1, #canvases do
push:addCanvas(canvases[i])
end
return self
end
function push:addCanvas(params)
table.insert(self.canvases, {
name = params.name,
private = params.private,
shader = params.shader,
canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT),
stencil = params.stencil or self._stencil
})
end
function push:setCanvas(name)
if not self._canvas then return true end
local canvasTable = self:getCanvasTable(name)
return love.graphics.setCanvas({ canvasTable.canvas, stencil = canvasTable.stencil })
end
function push:getCanvasTable(name)
for i = 1, #self.canvases do
if self.canvases[i].name == name then
return self.canvases[i]
end
end
end
function push:setShader(name, shader)
if not shader then
self:getCanvasTable("_render").shader = name
else
self:getCanvasTable(name).shader = shader
end
end
function push:initValues()
self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1

self._SCALE = {
x = self._RWIDTH/self._WWIDTH * self._PSCALE,
y = self._RHEIGHT/self._WHEIGHT * self._PSCALE
}

if self._stretched then --if stretched, no need to apply offset
self._OFFSET = {x = 0, y = 0}
else
local scale = math.min(self._SCALE.x, self._SCALE.y)
if self._pixelperfect then scale = math.floor(scale) end

self._OFFSET = {x = (self._SCALE.x - scale) * (self._WWIDTH/2), y = (self._SCALE.y - scale) * (self._WHEIGHT/2)}
self._SCALE.x, self._SCALE.y = scale, scale --apply same scale to X and Y
end

self._GWIDTH = self._RWIDTH * self._PSCALE - self._OFFSET.x * 2
self._GHEIGHT = self._RHEIGHT * self._PSCALE - self._OFFSET.y * 2
end
function push:apply(operation, shader)
self._drawFunctions[operation](self, shader)
end
function push:start()
if self._canvas then
love.graphics.push()
love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil })
else
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._WHEIGHT*self._SCALE.y)
love.graphics.push()
love.graphics.scale(self._SCALE.x, self._SCALE.y)
end
end
function push:applyShaders(canvas, shaders)
local _shader = love.graphics.getShader()
if #shaders <= 1 then
love.graphics.setShader(shaders[1])
love.graphics.draw(canvas)
else
local _canvas = love.graphics.getCanvas()
local _tmp = self:getCanvasTable("_tmp")
if not _tmp then --create temp canvas only if needed
self:addCanvas({ name = "_tmp", private = true, shader = nil })
_tmp = self:getCanvasTable("_tmp")
end
love.graphics.push()
love.graphics.origin()
local outputCanvas
for i = 1, #shaders do
local inputCanvas = i % 2 == 1 and canvas or _tmp.canvas
outputCanvas = i % 2 == 0 and canvas or _tmp.canvas
love.graphics.setCanvas(outputCanvas)
love.graphics.clear()
love.graphics.setShader(shaders[i])
love.graphics.draw(inputCanvas)
love.graphics.setCanvas(inputCanvas)
end
love.graphics.pop()
love.graphics.setCanvas(_canvas)
love.graphics.draw(outputCanvas)
end
love.graphics.setShader(_shader)
end
function push:finish(shader)
love.graphics.setBackgroundColor(unpack(self._borderColor))
if self._canvas then
local _render = self:getCanvasTable("_render")
love.graphics.pop()
local white = love11 and 1 or 255
love.graphics.setColor(white, white, white)
--draw canvas
love.graphics.setCanvas(_render.canvas)
for i = 1, #self.canvases do --do not draw _render yet
local _table = self.canvases[i]
if not _table.private then
local _canvas = _table.canvas
local _shader = _table.shader
self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader })
end
end
love.graphics.setCanvas()

--draw render
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
local shader = shader or _render.shader
love.graphics.push()
love.graphics.scale(self._SCALE.x, self._SCALE.y)
self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader })
love.graphics.pop()
--clear canvas
for i = 1, #self.canvases do
love.graphics.setCanvas(self.canvases[i].canvas)
love.graphics.clear()
end
love.graphics.setCanvas()
love.graphics.setShader()
else
love.graphics.pop()
love.graphics.setScissor()
end
end
function push:setBorderColor(color, g, b)
self._borderColor = g and {color, g, b} or color
end
function push:toGame(x, y)
x, y = x - self._OFFSET.x, y - self._OFFSET.y
local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT

x = (x >= 0 and x <= self._WWIDTH * self._SCALE.x) and normalX * self._WWIDTH or nil
y = (y >= 0 and y <= self._WHEIGHT * self._SCALE.y) and normalY * self._WHEIGHT or nil

return x, y
end
--doesn't work - TODO
function push:toReal(x, y)
return x + self._OFFSET.x, y + self._OFFSET.y
end
function push:switchFullscreen(winw, winh)
self._fullscreen = not self._fullscreen
local windowWidth, windowHeight = love.window.getDesktopDimensions()

if self._fullscreen then --save windowed dimensions for later
self._WINWIDTH, self._WINHEIGHT = self._RWIDTH, self._RHEIGHT
elseif not self._WINWIDTH or not self._WINHEIGHT then
self._WINWIDTH, self._WINHEIGHT = windowWidth * .5, windowHeight * .5
end

self._RWIDTH = self._fullscreen and windowWidth or winw or self._WINWIDTH
self._RHEIGHT = self._fullscreen and windowHeight or winh or self._WINHEIGHT

self:initValues()

love.window.setFullscreen(self._fullscreen, "desktop")
if not self._fullscreen and (winw or winh) then
windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions
end
end
function push:resize(w, h)
if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end
self._RWIDTH = w
self._RHEIGHT = h
self:initValues()
end
function push:getWidth() return self._WWIDTH end
function push:getHeight() return self._WHEIGHT end
function push:getDimensions() return self._WWIDTH, self._WHEIGHT end
return push

其他一切都可以在互联网上得到。删掉声音片段,代码就能运行了。这将会很有帮助。

我不确定如何编写此代码,但我有类似的情况,这可能会起作用。这就是你限制球的运动的地方,你可以这样说如果(ball_x比;10或ball_x <0),那么ball_x_动量= - ball_x_动量结束常数是方框的边界。然后在y方向重复代码。我希望这对你有帮助。

哇,这么多代码。

假设你需要球从窗口的顶部和底部反弹(如果你需要它从窗口的侧面反弹,尝试将self.y更改为self.xlove.graphics.getHeight更改为love.graphics.getWidth)

尝试添加:

if self.y < 0 then -- if the ball is touching the top of the screen
self.y = 0
self.dy = math.abs(self.dy)
end

并添加:

if self.y > love.graphics.getHeight() then -- if the ball is touching the bottom of the screen
self.y = love.graphics.getHeight()
self.dy = math.abs(self.dy)
end

这段代码应该放到ball.lua文件中,在Ball:update(dt)

希望这对你有帮助。

相关内容

  • 没有找到相关文章

最新更新