I am writing a game where i have a Tank and every tank can shoot bullets. And both Tank and Bullet class have coordinates and direction. And when we shoot a bullet it gets the coordinates of the tank, but when I change the coordinates of the bullet in the move method, the coordinates of the tank change also. How can I avoid this memory sharing, without making a lot of variables ?
class Tank:
def __init__(self, coords, direction):
self.coords = coords
self.direction = direction
self.bullet = None
def shoot_bullet(self):
self.bullet = Bullet(coords, direction)
class Bullet:
def __init__(self, coords, direction):
self.coords = coords
self.direction = direction
def _move(self):
self.coords[0] += 4
I guess coords is a list. So when you pass the list from Tank to Bullet, you operate on a single list, thus a change will be "visible" inside Tank and Bullet.
Either copy that list:
def shoot_bullet(self):
self.bullet = Bullet(self.coords[:], self.direction)
or use another datastructure, like a tuple or, since you use pygame, a Rect.
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
There is a grouped angle (two lines and a arc) moving and rotating at the same time, depending on the location of another Dot, from the video, the grouped angle is lagged behind the Dot, don't know why? below is the code:
class MovRot(Scene):
def construct(self):
ln = Line(LEFT*2, LEFT*1+UP*1.5)
self.add(ln)
ln_ab = Line(LEFT*2, interpolate(LEFT*2, LEFT*1+UP*1.5, 0.7), color=RED_D)
ln_bc = Line(LEFT*2, ORIGIN, color=RED_D)
arc_b = Arc(0, ln_ab.get_angle(), radius=0.6, color=RED_D).move_arc_center_to(LEFT*2)
grp_b = VGroup(ln_ab, ln_bc, arc_b)
self.add(grp_b)
dt = Dot(color=YELLOW_D).move_to(LEFT*2)
grp_1 = grp_b.copy()
def update_grp1(mob, alpha):
mob.become(grp_1)
mob.move_to(dt.get_center(), aligned_edge=arc_b.get_arc_center())
mob.rotate(alpha*PI, about_point=arc_b.get_arc_center())
self.add(grp_1, dt)
self.play(UpdateFromAlphaFunc(grp_b, update_grp1, rate_func=smooth), dt.move_to, LEFT*1+UP*1.5,
rate_func=smooth,
run_time=2)
Thanks for any help.
You always have to write the "master" animation at the beginning of the play method, in your case, the animation that "takes the baton" is move_to, so it must be the first to be written:
self.play(
dt.move_to, LEFT*1+UP*1.5,
UpdateFromAlphaFunc(grp_b, update_grp1, rate_func=smooth),
rate_func=smooth,
run_time=2
)
I am new to SceneKit and I am programming a game.
I have loaded two objects into my scene. The first object doesn't move, only the second one. The two objects always have to stick together but the second object can move completely free on the first object's surface depending on user input (basically like two magnets with infinity power but no friction).
My approach is to take the second object's x and y coordinates and look what object one's z coordinate is at given x and y coordinates. Then I move object two to the exact same z-coordinate.
I tried using a SCNDistanceConstraint but it didn't have any effect:
let cnstrnt = SCNDistanceConstraint(target: object1)
cnstrnt.maximumDistance = 1
cnstrnt.minimumDistance = 0.99
object2?.constraints?.append(cnstrnt)
I also tried using a SCNTransformConstraint without any effect either:
let transform = SCNTransformConstraint.positionConstraint(inWorldSpace: true) { (object2, vector) -> SCNVector3 in
let z = object1?.worldPosition.z
return SCNVector3(object2.worldPosition.x, object2.worldPosition.y, z!)
}
object2?.constraints?.append(transform)
Using a hitTest only returns results that are positioned on the bounding box of the object and not its actual surface:
let hitTest = mySceneView.scene?.physicsWorld.rayTestWithSegment(from: SCNVector3((object2?.position.x)!, (object2?.position.y)!, -10), to: (object2?.position)!, options: nil)
So how can I get the z-coordinate of an 3d object's surface from a x and y coordinate? Because then I'd be able to set the new position of object2 manually.
Maybe you have another approach that is more elegant and faster than mine?
Thanks beforehand!
Im fighting here with the so called ghost collisions on a simple tile based map with a circle as player character.
When applying an impulse to the circle it first starts bouncing correctly, then sooner or later it bounces wrong (wrong angle).
Looking up on the internet i read about an issue in Box2D (i use iOS Swift with Box2d port for Swift).
Using b2ChainShape does not help, but it looks i misunderstood it. I also need to use the "prevVertex" and "nextVertex" properties to set up the ghost vertices.
But im confused. I have a simple map made up of boxes (simple square), all placed next to each other forming a closed room. Inside of it my circle i apply an impulse seeing the issue.
Now WHERE to place those ghost vertices for each square/box i placed on the view in order to solve this issue? Do i need to place ANY vertex close to the last and first vertice of chainShape or does it need to be one of the vertices of the next box to the current one? I dont understand. Box2D's manual does not explain where these ghost vertices coordinates are coming from.
Below you can see an image describing the problem.
Some code showing the physics parts for the walls and the circle:
First the wall part:
let bodyDef = b2BodyDef()
bodyDef.position = self.ptm_vec(node.position+self.offset)
let w = self.ptm(Constants.Config.wallsize)
let square = b2ChainShape()
var chains = [b2Vec2]()
chains.append(b2Vec2(-w/2,-w/2))
chains.append(b2Vec2(-w/2,w/2))
chains.append(b2Vec2(w/2,w/2))
chains.append(b2Vec2(w/2,-w/2))
square.createLoop(vertices: chains)
let fixtureDef = b2FixtureDef()
fixtureDef.shape = square
fixtureDef.filter.categoryBits = Constants.Config.PhysicsCategory.Wall
fixtureDef.filter.maskBits = Constants.Config.PhysicsCategory.Player
let wallBody = self.world.createBody(bodyDef)
wallBody.createFixture(fixtureDef)
The circle part:
let bodyDef = b2BodyDef()
bodyDef.type = b2BodyType.dynamicBody
bodyDef.position = self.ptm_vec(node.position+self.offset)
let circle = b2CircleShape()
circle.radius = self.ptm(Constants.Config.playersize)
let fixtureDef = b2FixtureDef()
fixtureDef.shape = circle
fixtureDef.density = 0.3
fixtureDef.friction = 0
fixtureDef.restitution = 1.0
fixtureDef.filter.categoryBits = Constants.Config.PhysicsCategory.Player
fixtureDef.filter.maskBits = Constants.Config.PhysicsCategory.Wall
let ballBody = self.world.createBody(bodyDef)
ballBody.linearDamping = 0
ballBody.angularDamping = 0
ballBody.createFixture(fixtureDef)
Not sure that I know of a simple solution in the case that each tile can potentially have different physics.
If your walls are all horizontal and/or vertical, you could write a class to take a row of boxes, create a single edge or rectangle body, and then on collision calculate which box (a simple a < x < b test) should interact with the colliding object, and apply the physics appropriately, manually calling the OnCollision method that you would otherwise specify as the callback for each individual box.
Alternatively, to avoid the trouble of manually testing intersection with different boxes, you could still merge all common straight edge boxes into one edge body for accurate reflections. However, you would still retain the bodies for the individual boxes. Extend the boxes so that they overlap the edge.
Now here's the trick: all box collision handlers return false, but they toggle flags on the colliding object (turning flags on OnCollision, and off OnSeparation). The OnCollision method for the edge body then processes the collision based on which flags are set.
Just make sure that the colliding body always passes through a box before it can touch an edge. That should be straightforward.
In below mentioned code, what exactly is the difference between sprite, body and ground? Why are there no parameters passes to createBody while declaring ground?
what is difference between edgeshape.new() and polygonshape.new()?
require "box2d"
b2.setScale(20)
-- this function creates a box sprite with 2 happy and sad children
local function createBoxSprite(sx, sy)
local happy = Bitmap.new(Texture.new("happy-box.png", true))
happy:setAnchorPoint(0.5, 0.5)
local sad = Bitmap.new(Texture.new("sad-box.png", true))
sad:setAnchorPoint(0.5, 0.5)
local sprite = Sprite.new()
sprite:addChild(happy)
sprite:addChild(sad)
sprite:setScale(sx, sy)
return sprite
end
-- this table holds the dynamic bodies and their sprites
local actors = {}
-- create world
local world = b2.World.new(0, 9.8)
-- create a ground body and attach an edge shape
local ground = world:createBody({})
local shape = b2.EdgeShape.new(-200,480,520,480)
ground:createFixture({shape = shape, density = 0})
-- every 3 seconds, we create a random box
local function onTimer()
local sx = math.random(70, 100) / 100
local sy = math.random(70, 100) / 100
local body = world:createBody{type = b2.DYNAMIC_BODY, position = {x = math.random(0, 320), y = -35}}
local shape = b2.PolygonShape.new()
-- box images are 70x70 pixels. we create bodies 1 pixel smaller than that.
shape:setAsBox(34.5 * sx, 34.5 * sy)
body:createFixture{shape = shape, density = 1, restitution = 0.1, friction = 0.3}
local sprite = createBoxSprite(sx, sy)
stage:addChild(sprite)
actors[body] = sprite
end
Sprite is an empty object to group other Sprite inherited objects as in your case happy and sad Bitmaps.
Body is a representation of the physical body in box2d, it does not have any visual representation, only numbers as dimensions (width and height), position, rotation and different forces. Usually inside ENTER_FRAME event you take this values as position and rotation and apply them to the Sprite inherited object, so it would move exactly like the body in box2d simulated world.
Ground is an empty dummy box2d object. If you don't provide parameters to createBody it means the defaults are used, which basically means you don't care what values it has. The most common example of such body usage is for joints.
To create a joint you usually need two bodies, but what if you would to attach body through joint to some random position in the air, or dynamic position as mouse cursor, well you can do it by creating dummy physics object
Here is an example: http://appcodingeasy.com/Gideros-Mobile/Dragging-Box2d-object-in-Gideros-Mobile
In case of your example, then this body is used for EdgeShape which is usually an arbitrary shape used for creating world boundaries or in this case simply ground, so the dynamic object won't fall outside the screen