I'm new to using both Lua and LOVE2D. I thought that a perfect way to get familiar with the tools would be to create a snake style game. currently I have most of the things up and running. I run into a problem when adding to the snake after it eats the food. My snake class currently consists of a table of cells, each cell has a gridX and gridY variable. I also have 2 global variable called lastGridX and lastGridY that keep track of the last position of the last cell in the snake class. I update the position of the cells in reverse order and set them equal to the position of the cell with 1 less index in the table. I update the first cell by just adding a constant value called SCALE, the size of each square on the grid, to it. My problem arises when the position of the position of the first cell is the same as the position of the food. For some reason my lastGridX and lastGridY also change to the x and y positions of the food even though I never change them myself. This makes it so that every cell has the same position which is obviously not what I want to happen. Any help would be appreciated thank you. Here is some of the code below.
function love.update(dt)
-- updates the snake's position in set intervals
moveTimer = moveTimer - dt
if moveTimer <= 0 then
lastGridX = snake[#snake].gridX
lastGridY = snake[#snake].gridY
-- updates the body of the snake
for i = #snake, 2, -1 do
snake[i].gridX = snake[i - 1].gridX
snake[i].gridY = snake[i - 1].gridY
end
-- updates the head of the snake
if direction == "right" then
snake[1].gridX = snake[1].gridX + SCALE
elseif direction == "left" then
snake[1].gridX = snake[1].gridX - SCALE
elseif direction == "up" then
snake[1].gridY = snake[1].gridY - SCALE
elseif direction == "down" then
snake[1].gridY = snake[1].gridY + SCALE
end
moveTimer = MOVE_TIMER_MAX
print(lastGridX, lastGridY)
print(snake[#snake].gridX, snake[#snake].gridY)
if snake[1].gridX == newFood.gridX and snake[1].gridY == newFood.gridY then
-- moves the food to a random location on the grid if it is eaten
newFood.gridX = math.random(0, 63) * SCALE
newFood.gridY = math.random(0, 35) * SCALE
-- adds a new body part to the end of the snake
newC = cell
newC.gridX = lastGridX
newC.gridY = lastGridY
print(lastGridX, lastGridY)
print(snake[#snake].gridX, snake[#snake].gridY)
os.execute("pause")
table.insert(snake, newC)
end
end
end
Related
Im trying to generate a random map using a matrix but I dont really know how. Here is the
function for the matrix. wMap and hMap are the width and height, and mapSprites is a table containing some ground sprites. Also how can I draw the matrix? Im sorry if this is too much of a question, but Im really in need for some help
function buildMap(wMap, hMap)
for i = 1, wMap do
mt[i] = {}
for j = 1, hMap do
mt[i][j] = math.random(mapSprites)
end
end
end
Generating a random map in any programming language will utilize two core concepts: The language's random function and nested for loops, two for the case of a map/matrix/2d array.
The first problem, is you may or may not have mt initialized outside the function. This function assumes the variable exists outside of the function and each time the function is called it will overwrite mt (or initialize it for the first function call) with random values.
The second problem, the width, wMap, and height, hMap, of the map are in the wrong order, as maps/matrices/2d arrays first iterate over the height (y dimension) and then the width (x dimension).
The last problem, mapSpripes also has to be declared outside the function (which is not clear with your code snippet), which will be the highest possible value the random function can generate. You can read more about math.random here: http://lua-users.org/wiki/MathLibraryTutorial
Consider this function I wrote that makes those adjustments as well as has some additional variables for the minimum and maximum random value. Of course, you can remove these to have it fit your intended purposes.
function buildMap(wMap, hMap)
local minRand = 10
local maxRand = 20
for y = 1, hMap do
matrix[y] = {}
for x = 1, wMap do
matrix[y][x] = math.random(minRand, maxRand)
end
end
end
I suggest you use this function as inspiration for your future iteratins. You can make minRand and maxRand parameters or make matrix a returned value rather than manipulating an already declared matrix value outside of the function.
Best of luck!
EDIT:
Regarding your second question. Look back at the section I wrote about nested for loops. This will be crucial to "drawing" your map. I believe you have the building blocks to resolve this issue yourself as there isn't enough context provided about what "drawing" looks like. Here is a fundamentally similiar function, based on my previous function, on printing the map:
function printMap(matrix)
for i = 1, #matrix do
for j = 1, #matrix[i] do
io.write(matrix[i][j] .. " ")
end
io.write("\n")
end
end
For choosing random sprite, I recommend you to create a table of sprites and then save index of sprite in matrix. Then you can draw it in same loop, but now, you will iterate over matrix and draw sprite based on sprite index saved in matrix in position given by matrix position (x and y in loop) times size of sprite.
local sprites, mt = {}, {}
local spriteWidth, spriteHeight = 16, 16 -- Width and height of sprites
function buildMap(wMap, hMap)
mt = {}
for i = 1, wMap do
mt[i] = {}
for j = 1, hMap do
mt[i][j] = math.random(#sprites) -- We choose random sprite index (#sprites is length of sprites table)
end
end
end
function love.load()
sprites = {
love.graphics.newImage('sprite1.png'),
love.graphics.newImage('sprite2.png'),
-- ...
}
buildMap()
end
function love.draw()
for y, row in ipairs(mt) do
for x, spriteIndex in ipairs(row) do
-- x - 1, because we want to start at 0, 0, but lua table indexing starts at 1
love.graphics.draw(sprites[spriteIndex], (x - 1) * spriteWidth, (y - 1) * spriteHeight)
end
end
end
I'm creating a 2D platform game using Corona SDK and I'm stuck with collisions.
Basically there is a character that runs over this ground made of blocks. This is because sometimes there can be holes in the ground.
The game is an endless one, so as the character moves forward new blocks (and holes) are dynamically added - and also removed if they goes off screen.
It works nicely but this approach works against the collision system, let me explain how.
Now that I have the ground in place I want the character to jump but only if it is touching the ground, to avoid jumping while in air.
Whenever a collision is detected between character and ground an event is fired - two times. The first time when the character is entering a ground block and the second time when the character leaves it. So when the character lands on the ground a isGround Boolean is set to true. And when - after a jump - it leaves it the flag is set to false. The problem is that every time it exits a block to enter another - walking along the ground without jumping - the flag get updated. This makes the jump based on the isGround flag less reliable. Sometimes it happens that you can't jump because isGround == false though the character is on the ground.
Ground block creation snippet
-- init() method set the sprite of the ground block and physic to that sprite
function GroundBlock:init()
self.sprite = display.newImageRect(self.path, self.width, self.height)
self.sprite.x = self.x
self.sprite.y = self.y
physics.addBody(self.sprite, 'static', {
density = 0,
friction = 0,
bounce = 0,
box = {
halfWidth = self.width / 2,
halfHeight = self.height / 2,
y = 16,
x = 0
}
})
local collisionObj = {
name = 'ground'
}
self._collision = collisionObj
self.sprite._collision = collisionObj
self.isShow = true
end
Ground placing GroundBlocks snippet
-- init() method initialize the ground with a fixed number of blocks
function Ground:init()
self.offsetX = 0
while self.offsetX < self.camera.borderRight * 2 do
self._createBlock(1)
end
self.lastCameraPos = self.camera.borderRight
end
-- update() is called once per frame
function Ground:update()
if (self.camera.borderRight - self.lastCameraPos > self._blockWidth) then
local rand = math.ceil(math.random() * 10) % 2
if self._skippedBlock >= 2 or rand == 0 then
self._createBlock(1)
self._skippedBlock = 0
else
self._createBlock(0)
self._skippedBlock = self._skippedBlock + 1
end
self.lastCameraPos = self.camera.borderRight
end
for i, block in ipairs(self.blocks) do
if block.sprite.x < self.camera.borderLeft - block.width then
table.remove(self.blocks, i)
self.camera:remove(block.sprite)
block:delete()
end
end
end
Collision detection snippet
function Character:collision(event)
if ( event.phase == "began" ) then
if event.other._collision.name == "ground" then
self.isGround = true
end
elseif ( event.phase == "ended" ) then
if event.other._collision.name == "ground" then
self.isGround = false
print('nope')
end
end
end
A solution would be to make a ground as a single imgRect but how to make holes in it?
You could simplify your code and prevent this issue from ever occurring by tracking if the character can jump instead of tracking if the character is on the ground.
For instance,
function jump( event )
if event.phase == "began" then
if canJump then
canJump = false
-- your code that makes the player jump
end
end
end
You probably use touches to determine whether the player character jumps, right? This way, you'll trigger the jump when the touch starts as long as the character has not already jumped.
You could then reset this value in your collision function by editing it slightly:
function Character:collision(event)
if event.phase == "began" then
if event.other._collision.name == "ground" then
canJump = true
end
end
end
This way, the character's ability to jump is determined by whether or not the player has pressed jump already and if the character has hit the ground since the last jump.
This kind of approach also gives you the ability to pivot towards implementing mechanics like double jump. If instead of using a boolean canJump variable you chose to use a number variable, e.g. jumpsLeft, you could reduce the number of jumps left every time the character jumps and only let the character jump if jumpsLeft is larger than 0. Then you'd simply reset the value back to 1 (or whatever you'd want upon hitting the ground).
Hey so I am looking for something similar to in tennis when players challenge a point and the video shows whether the ball was in or out by zooming in really really close to the moment when the ball lands to see whether a fraction was on the line.
I have experimented with using transitions with xScale and yScale but the results I get is strange, almost as if the objects have moved during zooming in. If there was a way to lock in position and then zoom, that would work. The second method I tried is putting the graphics into a display group and then scaling the group. This also results in weird behaviour where the whole group begins moving diagonally across the screen.
Please help as this is confusing me
cheers.
Objects which will scale:
cloud = display.newImageRect("cloud.png", 419,273)
cloud.anchorY = 0
cloud.anchorX = 0.5
cloud.alpha = 1
cloud.x = display.contentCenterX
cloud.y = display.contentCenterY + 250
physics.addBody(cloud, {isSensor=true})
star = display.newImageRect("Star.png", 78,72)
star.anchorY = 0
star.anchorX = 0.5
star.alpha = 1
star.name = "Star"
physics.addBody(star, {isSensor=true})
star.x = display.contentCenterX
star.y = display.actualContentHeight - display.actualContentHeight - 100
Scale Function
function scale( event )
transition.to(star, {time=2000, xScale=1.5, yScale = 1.5})
transition.to(cloud, {time=2000, xScale=1.5, yScale=1.5})
end
When you scale an object, it may "move" due to its anchor position, which is the position of the object that is used to position it and also works as its 'anchor' during rotation or scale.
So, you have 2 options:
1) Set the anchor position (obj.anchorX = value , obj.anchorY = value ) to the one that will make your object stays in the position that you want;
2) During the transition, also change its x and y to compensate the moving.
I'm trying to make walls to change size based on the distance from the player's torso (bigger when close, smaller when faraway). I'm not really good at this kind of stuff so I have no idea how to do this.
Here's the code at the moment:
for _, v in pairs(script.Parent:GetChildren()) do
if string.sub(v.Name,1,4) == "Wall" then
local walls = {}
walls[v] = v.CFrame
for x,y in pairs(walls) do
print(x,y)
end
local startCFrame = v.CFrame
game:GetService("RunService").RenderStepped:connect(function()
v.Size = v.Size + Vector3.new(0,(workspace["Player"].Torso.Position-v.Position).magnitude,0)
v.CFrame = walls[v] * CFrame.new(0,v.Size.Y/2-(script.Parent.Floor.Size.Y/2),0)
end)
end
end
if you're wondering why im changing the cframe of v, it's so that when the player walks over the brick or into the side of it, it won't go above the player, but stay in the same position it originally was
I've been trying to make a game where you're in a square and when you go to the sides, parts come up and block you.
I've gotten far to the point where it's working fine, except for a few problems:
the parts go below the square when not raised, I want them to be visible when they're not raised
the parts go down when you jump, making it easy to escape.
the parts go up too early
This is the code that deals with the wall positioning.
for _, v in pairs(model:GetChildren()) do
if string.sub(v.Name,1,4) == "Wall" then
local walls = {}
walls[v] = {v.CFrame,Vector3.new(1, 1, 1)}
game:GetService("RunService").RenderStepped:connect(function()
if(workspace[game.Players.LocalPlayer.Name]:FindFirstChild("HumanoidRootPart")) then
local mag = (v.Position - workspace[game.Players.LocalPlayer.Name]:FindFirstChild("HumanoidRootPart").Position).magnitude
sizeFactor = math.floor(mag)
v.CFrame = walls[v][1]*CFrame.new(0,-sizeFactor+(walls[v][1].Y*1.8),0)
end
end)
end
end
You can see my game here: https://www.roblox.com/games/400391033/Marble-walls
See commented code.
for _, v in pairs(model:GetChildren()) do
if string.sub(v.Name,1,4) == "Wall" then
local walls = {}
walls[v] = {v.CFrame,Vector3.new(1, 1, 1)}
game:GetService("RunService").RenderStepped:connect(function()
if(workspace[game.Players.LocalPlayer.Name]:FindFirstChild("HumanoidRootPart")) then
local mag = (v.Position - workspace[game.Players.LocalPlayer.Name]:FindFirstChild("HumanoidRootPart").Position).magnitude
if (mag <= 2) then --[[
Currently your issue is that you never actually do ANYTHING regarding magnitude
you essentially change the y-Axis as soon as the player spawns.. hence why it does it too early
kappa
]]
sizeFactor = math.floor(mag)
v.CFrame = walls[v][1]*CFrame.new(0,-sizeFactor+(walls[v][1].Y*1.8),0)
end;
end
end)
end
end