How can I get values from multiple instances of a class? - lua

I am making a roguelike in Love2D as a hobby project. My approach is to try and use as much of the native capabilities of Lua and the Love2D (0.10.1) API as possible, without relying on fancy libraries like middleclass or HUMP, so as to learn more about the language.
After reading PiL's chapters on OOP and seeing the power there, I decided to set up a Mob class (using metamethods to emulate class functionality) that encompasses the players, monsters, and other NPCs (anything that can move). So, far, it's working beautifully, I can create all kinds of instances easily that share methods and all that stuff. But there's a lot of things I don't know how to do, yet, and one of them is holding my prototype up from further progress.
Setting up collision with the map itself wasn't too bad. My maps are tables full of tables full of integers, with 0 being the floor. The game draws "." and "#" and "+" and such to denote various inanimate objects, from each table. Player 1 moves using the numpad, and their position is tracked by dividing their raw pixel position by 32 to create a grid of 32x32 "tiles". Then, inside love.keypressed(key), I have lines like:
if key == "kp8" and currentmap[player1.grid_y - 1][player1.grid_x] == 0 then
player1.grid_y = player1.grid_y - 1
and so on, with elseifs for each key the player can press. This prevents them from walking through anything that isn't an open floor tile in the map itself.
But, I'm trying to implement some kind of "collision detection" to prevent MOBs from walking through each other and to use in writing the rules for combat, and this is trickier. I had a method in place to calculate the distance between mobs, but I'm told this might eventually cause rounding errors, plus it had to be written for each combination of mobs I want to test, individually.
What I'd like to know is: Is there a known (preferably elegant) way to get all instances of a particular class to pass some number of values to a table?
What I'd like to do is "ask" every Mob on a given map where they are, and have them "report" self.grid_x and self.grid_y to another layer of map that's just for tracking mobs (1 if self.is_here is true, 0 if not, or similar), that gets updated every turn. Then, I could implement collision rules based on coordinates being equal, or maybe a foo.is_here flag or something.
I have only vague ideas about how to proceed, however. Any help would be appreciated, including (and maybe especially) feedback as to a better way to do what I'm trying to do. Thanks!

A simple idea is to store "who is here" information for every cell of the field and update this information on every move of every object.
function create_game_field()
-- initialize a table for storing "who is here" information
who_is_here = {}
for y = 1,24 do
who_is_here[y] = {}
for x = 1,38 do
who_is_here[y][x] = 0
end
end
end
function Mob:can_move(dx, dy)
local u = currentmap[self.y + dy][self.x + dx]
local v = who_is_here[self.y + dy][self.x + dx]
if u == 0 and v == 0 then
return true
else
end
end
function Mob:move(dx, dy)
-- update "who is here"
who_is_here[self.y][self.x] = 0
self.x, self.y = self.x + dx, self.y + dy
who_is_here[self.y][self.x] = 1
end
function Mob:who_is_there(dx, dy) -- look who is standing on adjacent cell
return who_is_here[self.y + dy][self.x + dx] -- return mob or nil
end
function Mob:roll_call()
who_is_here[self.y][self.x] = 1
end
Usage example:
-- player1 spawns in at (6,9) on the grid coords
player1 = Mob:spawn(6,9)
-- player1 added to who_is_here
player1:roll_call()
Then, in love.keypressed(key):
if key == "kp8" and player1:can_move(0, -1) then
player1:move(0, -1)
end

There are a few ways you could get all your instances data but one of the simpler ones is probably to have them all be added to a table when they are created. Providing you add the entire table for that instance, all the values will update in the main table because it acts like a collection of pointers.
function mob:new( x, y, type )
self.x = 100
self.y = 200
self.type = type
-- any other declarations you need
table.insert(allMobs, self)
return self
end
Here we insert all the mobs into the table 'allMobs'. Once we have that we can simply iterate through and get all our coordinates.
for i, v in ipairs(allMobs) do
local x, y = v.x, v.y
-- Do whatever you need with the coordinates. Add them to another table, compare
-- them to others, etc.
end
Now we have a table with all our mobs in it and a way to access each of their positions. If you have any further inquiries then let me know.

Related

Gmod | Entity Spawning

if SERVER then
function SWEP:PrimaryAttack()
if self.Owner:GetEyeTrace().HitPos:Distance(self.Owner:GetPos()) < 100 then
local entity = ents.Create( "gred_emp_grw34" )
if ( !IsValid( entity ) ) then return end
entity:SetPos( self.Owner:GetEyeTrace().HitPos )
local entang = self.Owner:GetAngles()
entity:SetAngles(Angle(0, entang.y, 0) +Angle(0, 180, 0))
entity:SetModel("models/props_artillery/german/r_mortar_gw34.mdl" )
entity:Spawn()
self.Owner:StripWeapon( "turret_entplace" )
end
end
function SWEP:SecondaryAttack() end
end
This is my Code for spawning an entitiy with a Weapon now the problem is the Entity spawns in the floor so i am trying to add some Height to it can anyone help me with that?
The entity's position is self.Owner:GetEyeTrace().HitPos and because you are likely looking at the floor the entity spawn in the floor. I think you have something like this.
What you need to do is offsetting the position. It can be done like this :
local z_offset = 5 -- the offset you need (depends on the entity)
hitPos = self.Owner:GetEyeTrace().HitPos -- the position of the eye's hitting point
spawnPos = hitPos:Add(Vector(0, 0, z_offset) -- offset the pos
entity:SetPos( spawnPos )
Concerning the offset, i can't find anything (on the gmod wiki) to determine it... I think you will need to try different offset... Also, if you have other entities to spawn, you may also need to make a table with the different offsets.

Variable in Player State in Lua not updating as expected

In the following code, the value of self.player.x is assigned to self.x is assigned at init and tweaked a little bit to the left or right when the state is entered. The PlayerPilotState then manipulates self.player.x every frame and my expectation is that self.x would update along with it. Items such as the tilemap, which are assigned to player as def.tilemap when the player is instantiated, do update as the map changes. How do I ensure that variables such as self.x will update themselves as well? I know that I can update that variable under update every frame, but it seems ineloquent and since I'm fairly new to coding, I don't understand why the player.tilemap does update whereas this variable doesn't. Thank you!
function PlayerPilotState:init(pilot, passenger)
self.player = pilot
self.passenger = passenger
self.animation = Animation {
frames = {2, 3, 2, 8},
interval = 0.1
}
self.player.currentAnimation = self.animation
-- x value at middle of players to make collisions more readable
-- and scalable (for left/right pilot/passenger cases)
self.x = self.player.x
end
function PlayerPilotState:enter(params)
-- determine which side passenger is riding on
self.ridingSide = params.ridingSide
if self.ridingSide == 'left' then
self.x = self.player.x - 1/2 * self.player.width
else
self.x = self.player.x + 1/2 * self.player.width
end
end
This is a common misconception for people coming from non-scripted languages.
Note that self.x and self.player.x are numbers so when you use assignment:
assert(type(self.player.x) == "number")
self.x = self.player.x -- copy
you are making a copy of that number.
On the other hand when dealing with tables or userdata objects then assignment works by making a reference:
assert(type(self.player) == "table")
self.ref = self.player -- reference
Generally speaking you can't really have one number synchronized in the way you have described. It wouldn't be efficient either because you will have to make a redundant "copy" of that value in memory.
This is a question of encapsulation and how/where your data is stored. If you are not sure how to redesign your code remember that "values that change together, belong together". Good luck!

Nested For-Loops - Remove Both Elements i and j

I'm using a game-making framework implemented in Lua to keep busy in quarantine. I'm making a simple platformer with ECS/DOP, and I wanted to generate collision geometry derived from a tilemap rather than just checking for collisions with all tiles.
Each tile has a bounding box component that points to a list that contains the basic shapes. Each shape stores the edges of the bounding box as {{x1,y1},{x2,y2}}. The first step in this process is to parse a TileMap table that contains only tilenames, then insert a copy of the corresponding bounding-box translated by the row/column * grid_dimension into a table named BBOX. The next step is to delete all instances of an edge if it is a duplicate illustrated by this image
which is where I'm stuck.
The desired, basic edge-deletion algorithm looks like this:
for i = #BBOX, 1, -1 do
local edge1 = BBOX[i]
for j = i, 1, -1 do
local edge2 = BBOX[j]
same_edge = edge1 == edge2 -- Not the actual comparison, just the outcome of it
if same_edge and i ~= j then
BBOX[i] = nil
BBOX[j] = nil
end
end
end
The issue is of course that this errors when i is equal to a j that was removed earlier in the loop. I've looked around and haven't been able to find a way to remove all instances of duplicate values in lua, only solutions that care about uniqueness. Has anyone found an efficient method of doing this or something similar?
If you use the value unique to each key you can better detect duplicates.
we will call this unique value edge.key.
If an edge will only exist at most twice then:
BBOX[edge.key] = BBOX[edge.key] == nil and edge or nil
will do the trick.
But where an edge may exist any arbitrary number of time we can do something like this:
local duplicateEdges = {}
for i = #BBOX, 1, -1 do
if BBOX[edge_key] then
duplicateEdges[edge_key] = true
else
BBOX[edge_key] = edge
end
for j = i, 1, -1 do
if BBOX[edge_key] then
duplicateEdges[edge_key] = true
else
BBOX[edge_key] = edge
end
end
end
for k in pairs(duplicateEdges) do
BBOX[k] = nil
end

joining joints in corona using lua

for i = 1,5 do
local link = {}
for j = 1,20 do
link[j] = display.newImage( "link.png" )
-
List item
link[j].x = 121 + (i*34)
link[j].y = 55 + (j*17)
physics.addBody( link[j], { density=2.0, friction=0, bounce=0 } )
-- Create joints between links
if (j > 1) then
prevLink = link[j-1] -- each link is joined with the one above it
else
prevLink = beam -- top link is joined to overhanging beam
end
myJoints[#myJoints + 1] = physics.newJoint( "pivot", prevLink, link[j], 121 + (i*34), 46 + (j*17) )
end
end
What does this mean?
From the Corona SDK's manual:
A pivot joint, known as a revolute joint in Box2D terms, joins two
bodies at an overlapping point, like two boards joined by a rotating
peg. The initial arguments are bodies A and B to join, followed by the
x and y coordinates for the anchor point, declared in content space
coordinates.
local pivotJoint = physics.newJoint( "pivot", bodyA, bodyB, anchor_x, anchor_y )
So the values you ask about are anchor coordinates (the point where both bodies are linked)
The number values are just values someone picked to make it look like he wanted it to.
Just alter them and see the links move!
That's a general advice. If you don't know what something does, refer to the manual and if you cannot break anything (which is the case pretty much always) try what happens if you change it.
That's the only way you can understand things.

How to effectively handle object removal of objects with Corona SDK

just starting to play around with the awesome corona sdk.
I started building a simple shooter game.
I have the following code :
-- Global Variables
local shot = audio.loadSound('shot.mp3')
local bg = display.newImage('bg.png')
local shoot = {}
local Main = {}
local Init = {}
local bullets = display.newGroup()
function update()
if(bullets.numChildren ~= 0) then
for i = 1, bullets.numChildren do
bullets[i].y = bullets[i].y - 8
-- Destroy Offstage Bullets
if(bullets[i].y < (-bullets[i].height-5)) then
-- bullets[i]:removeSelf()
bullets:remove(bullets[i])
display.remove(bullets[i])
return
end
end
end
end
-- Initialisation functions
function Init ()
display.setStatusBar(display.HiddenStatusBar)
local movieclip = require('movieclip')
local physics = require('physics')
physics.start()
physics.setGravity(0, 0)
end
function shoot:tap(e)
for i = 1, 15 do
local bullet = display.newImage('bullet.png')
bullet.x = 150
bullet.y = 470
bullet.name = 'bullet'
physics.addBody(bullet)
bullets.insert(bullets, bullet)
end
audio.play(shot)
end
-- Main routine
function Main ()
Init()
bg:addEventListener('tap', shoot)
Runtime:addEventListener('enterFrame', update)
end
Main()
For now it 'works'; but when the bullet comes of the screen the whole 'game' slows down and I can clearly see that each bullet is removed which slows down the game.
Maybe i'm not doing it right; also tried the :removeSelf() function; same results.
I was facing the same problem...And got the soln for this:
you are removing objects using for loop:
if you remove objects in for loop say: you deleted object at index 1, then object 2 moves to object 1...so when it loops to object to it wont check object 2(bcoz its moved to object 1's place and you are checking object 3)
for j = 1, buttonGroup.numChildren do
buttonGroup[1]:removeSelf(); --this removes all childrens
end
for j = 1, buttonGroup.numChildren do
buttonGroup[j]:removeSelf(); --this removes alternative childrens
end
I hope its useful
I fought for a long long time with object removal for my game which uses a lot of tabbed views. The solution I found was to use "= nil", ":removeSelf()" and ".alpha = 0" on all objects which need removing. If they are all added to the same group, and nothing else is in it, that could be used too, but doesn't always work for various reasons as to how groups are set up behind the scenes.
It would seem what you've done there is remove the contents of "bullets" but that's just a reference, so where you say each bullet is being removed, it's actually only being dropped from the array - the object itself still exists and needs to be nil'd in order to prevent a memory leak and a slowing of the app (you will probably have found that each bullet slows the app more, right?)
Add this condition after your removes and you should be set:
if(not(bullet == nil)) then
bullet.alpha = 0;
bullet:removeSelf();
bullet = nil;
end
its easier to simply create a table like this
local bulletCache = {}
Then in your bullet creation code add
table.insert(bulletCache, myBulletObject)
then in your exit code and/or your destroy code say
for i = 1, #bulletCache do
--[[
--Below is not needed but just to keep your code consitant
pcall(function()
bullet.alpha = 0
end)
--]]
pcall(function()
bullet:removeSelf()
end)
pcall(function()
bullet = nil
end)
end
First of all, never try to execute any kind of loop in GameLoop. It does drop fps of game, because it takes usually more memory.
Now, it seems that you just want to destroy bullets after disappearing from the screen and you are using physics too, so why you are not taking advantage of that?
Here are the steps you should follow. (If you find any query or difficulty about it then ask me)
1.Draw a Static Physical Line little bit above the screen. Let say y is -20.
local _yLimit = -20
local _limitLine = display.newLine(0,_yLimit,display.contentWidth,_yLimit)
_limitLine.anchorX = 0
_limitLine.anchorY = 0
2.Add all bullets as a physical object.
3.Apply force at the Bottom Center of bullet, rather then transforming it. [Assuming that bullet have rectangular physical shape, otherwise decide the equilibrium point of the shape].
4.Check for collisions with "collision" event listener.
5.Destroy bullets on collision
Thats so simple :)
Let me know if you still have problems regarding this.

Resources