Diagonal movement causing tilemap painting flaws - diagonal

I was able to make a good assembly of what is happening and killing me for weeks, and I really wanted to know if it was my mistake or just the keys pressed too fast, or the rendering delayed? to deal with the problem, because I'm not finding the culprit for doing so.
This happens when for example I go from diagonal down-rigth to left, and right, then I go back down, I make moves like that, fast, it does not always happen, but enough to disrupt the game.
Movement Script:
extends KinematicBody2D
var velocity = 150
var direction = Vector2()
func _ready():
set_fixed_process(true)
func _fixed_process(delta):
#-----------MOVIMENT-------------------------------
direction = Vector2()
#LEFT
if Input.is_action_pressed("left"):
direction += Vector2(-1, 0)
#RIGHT
elif Input.is_action_pressed("right"):
direction += Vector2(1, 0)
#UṔ
if Input.is_action_pressed("up"):
direction += Vector2(0, -1)
#DOWN
elif Input.is_action_pressed("down"):
direction += Vector2(0, 1)
if direction.x != 0 || direction.y != 0:
direction = direction.normalized()*velocity
move(direction*delta)
Paint Script:
extends Control
var map_size = 64
var preScriptUtils = preload("res://scripts/utils_fuctions.gd")
var utils_functions
onready var player = $"../Player"
onready var tile_map = $"../TileMap"
var posTileMap = Vector2()
var posWorld = Vector2()
var prevRect = Rect2()
var newRect = Rect2()
var distance_gen = 2048
var positionMap_rect = 0
var size_rectMap = 0
func _ready():
utils_functions = preScriptUtils.utils_functions.new()
set_fixed_process(true)
posWorld = Vector2(0, 0)
positionMap_rect = ((distance_gen/32)/2)
size_rectMap = distance_gen/32
surround_map(posWorld)
func _fixed_process(delta):
var posPlayer = player.get_position()
posTileMap = tile_map.world_to_map(posPlayer) - Vector2(positionMap_rect, positionMap_rect)
newRect = Rect2(tile_map.map_to_world(posTileMap), Vector2(distance_gen, distance_gen))
if prevRect.position != newRect.position:
load_newMap()
func gen_data(var _posWorld=Vector2(0, 0), var _posTileMap=Vector2(0, 0), var size=Vector2(0, 0)):
var x = 0
var y = 0
var pos_modx = 0
var pos_mody = 0
while x < size.x:
y = 0
pos_modx = utils_functions.mod(_posWorld.x + x, map_size)
#NOISE
var result_noise
while y < size.y:
pos_mody = utils_functions.mod(_posWorld.y + y, map_size)
if (pos_modx >= 0 && pos_mody >= 0) && (pos_modx < map_size && pos_mody < map_size):
tile_map.set_cell(_posTileMap.x + x, _posTileMap.y + y, biomes(result_noise))
y += 1
x += 1
func load_newMap():
var rectIntersection = newRect.clip(prevRect)
var _x = 0
var _y = 0
#LEFT
if player.direction.x < 0:
if int((newRect.size.x - rectIntersection.size.x)/32) == 1:
_x = int(newRect.position.x/32)
_y = int(rectIntersection.position.y/32)
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(_x, _y), Vector2(1, size_rectMap))
#RIGHT
elif player.direction.x > 0:
if int((newRect.size.x - rectIntersection.size.x)/32) == 1:
_x = int(rectIntersection.end.x/32)
_y = int(rectIntersection.position.y/32)
posWorld.x += 1
Vector2(_x, _y)
gen_data(Vector2(posWorld.x - 1, posWorld.y), Vector2(_x, _y), Vector2(1, size_rectMap))
#UP
if player.direction.y < 0:
if int((newRect.size.y - rectIntersection.size.y)/32) == 1:
_x = int(newRect.position.x/32)
_y = int(newRect.position.y/32)
posWorld.y -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(_x, _y), Vector2(size_rectMap, 1))
#DOWN
elif player.direction.y > 0:
if int((newRect.size.y - rectIntersection.size.y)/32) == 1:
_x = int(rectIntersection.position.x/32)
_y = int(rectIntersection.end.y/32)
posWorld.y += 1
gen_data(Vector2(posWorld.x, posWorld.y - 1), Vector2(_x, _y), Vector2(size_rectMap, 1))
prevRect = newRect
func surround_map(var _posWorld=Vector2(0, 0)):
posTileMap = - Vector2(positionMap_rect, positionMap_rect)
prevRect = Rect2(tile_map.map_to_world(posTileMap), Vector2(distance_gen, distance_gen))
gen_data(_posWorld, Vector2(-32, -32), Vector2(64, 64))
Do not be fussed with the codes, it's very simple what I'm trying to do, I want to move the player while I generate my world, it works perfectly in the 4 directions, but when it involves the diagonals, it does not go as expected. I'm painting like this, I have two rects that I compare to each frame, I take the retagunlo of the interception and calculate where I should paint or erase.
Video of what is happening:
http://www.dailymotion.com/video/x6286px
But there are these flaws not only by pressing too fast, it also happens in slow motion, while being diagonally down-right and then left or left-right and then back to right, moving on the diagonals seems to be the problem, I've tried a lot of shapes, I have almost a notebook all scribble trying to figure this out, I tried with diagonal checks too and I get the same result, today I lost another day, and it does not leave the place, I'm weeks like this, when I think I fixed it, not really.
I'm using godot.
Up 1:
With #Ryan1729 response I realized that I was really thinking the wrong way, but I try to fix my logic by doing something like below and the error continues, I'm confused:
if direction.x != 0 || direction.y != 0:
var vetor_normalized = direction.normalized()
if abs(vetor_normalized.x) < 1 && abs(vetor_normalized.y) < 1:
var vx = Vector2(direction.x, 0)
var vy = Vector2(0, direction.y)
vx = vx.normalized()*velocity
move(vx*delta)
vy = vy.normalized()*velocity
move(vy*delta)
else:
direction = direction.normalized()*velocity
move(direction*delta)
Up2:
Now I have done so, it seems that solved the flaws, but others appeared, which I still do not understand, this issue of 0 confuses me too much.
var oldPosWorld = Vector2(0, 0)
var rect_tileMap = Vector2(-32, -32)
func load_newMap():
var posPlayer = Vector2(int(player.get_position().x/32), int(player.get_position().y/32))
#LEFT
if posPlayer.x < oldPosWorld.x:
pos_tileMap.x -= 1
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(pos_tileMap.x, pos_tileMap.y), Vector2(1, size_rectMap))
#RIGHT
elif posPlayer.x > oldPosWorld.x:
pos_tileMap.x += 1
posWorld.x += 1
gen_data(Vector2(posWorld.x - 1, posWorld.y), Vector2(pos_tileMap.x + 63, pos_tileMap.y), Vector2(1, size_rectMap))
#UP
if posPlayer.y < oldPosWorld.y:
pos_tileMap.y -= 1
posWorld.y -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(pos_tileMap.x, pos_tileMap.y), Vector2(size_rectMap, 1))
#DOWN
elif posPlayer.y > oldPosWorld.y:
pos_tileMap.y += 1
posWorld.y += 1
gen_data(Vector2(posWorld.x, posWorld.y - 1), Vector2(pos_tileMap.x, pos_tileMap.y + 63), Vector2(size_rectMap, 1))
oldPosWorld = posPlayer
The movements of the right and bottom are right, the problem is those of the left and above that is left over this empty space(ie dealing with the negatives
).
If I put x - 1 on the left to solve the problem, this happens to me:
I solved this by changing:
var posPlayer = Vector2(int(player.get_position().x/32), int(player.get_position().y/32))
by checks like this:
if ((player.get_position().x)/32)
but now I get this by going to the sides and then up:
Up 3:
I'm not going to put my mind to it, so as not to get frustrated if I'm mistaken, but by the tests I did the failure stopped. The code would look like this by assigning int (player.get_position (). X / 32) separately to oldPosWorld.x, this respectively for y as well, since before I was assigning the position to the player only once the postPlayer variable, this caused errors , when you entered the up-down if, as it was still with the previous position of x, doing so updates everything in its right time.
is still so to the left and up:
I do not quite understand why, and frankly not so soon will I know, I'm too tired to look at it. But I'd like to understand not to err. Apart from this it seems to be happening now as expected.
#LEFT
if int(player.get_position().x/32) < oldPosWorld.x:
rect_tileMap.x -= 1
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(rect_tileMap.x, rect_tileMap.y), Vector2(1, size_rectMap))
oldPosWorld.x = int(player.get_position().x/32)

I think that the problem is that you want discrete movement (moving up and right moves 1 up and 1 right) but you're normalizing the vector to length 1 in the first code snippet. In the non-diagonal cases this does nothing because the vectors are length 1 already. But in the diagonal cases the vector is length sqrt(2) before normalization, (you can use the Pythagorean theorem to show this.) So your vector is getting shrunk down to something like Vector2(sqrt(2)/2, sqrt(2)/2)), (sqrt(2)/2 is about 0.707,) which causes the painting offset.
Edit: My best guess now is that the problem is that you are calling gen_data twice when the player moves diagonally. I think you want to figure out the x and y offsets then redraw the map.

Summarizing the error was in the update(which in the case would be oldPosWorld), because they were two blocks of ifs, I would enter one after another, and without updating the position, paints the wrong way because it is in a late position:
Video working:
http://www.dailymotion.com/video/x62e6l9

Related

How do I optimize this code so that it executes faster (max ~5 mins)?

I am trying to make a program in turtle that creates a Lyapunov fractal. However, as using timeit shows, this should take around 3 hours to complete, 1.5 if I compromise resolution (N).
import turtle as t; from math import log; from timeit import default_timer as dt
t.setup(2000,1000,0); swid=t.window_width(); shei=t.window_height(); t.up(); t.ht(); t.tracer(False); t.colormode(255); t.bgcolor('pink')
def lyapunov(seq,xmin,xmax,ymin,ymax,N,tico):
truseq=str(bin(seq))[2:]
for x in range(-swid//2+2,swid//2-2):
tx=(x*(xmax-xmin))/swid+(xmax+xmin)/2
if x==-swid//2+2:
startt=dt()
for y in range(-shei//2+11,shei//2-1):
t.goto(x,y); ty=(y*(ymax-ymin))/shei+(ymax+ymin)/2; lex=0; xn=prevxn=0.5
for n in range(1,N+1):
if truseq[n%len(truseq)]=='0': rn=tx
else: rn=ty
xn=rn*prevxn*(1-prevxn)
prevxn=xn
if xn!=1: lex+=(1/N)*log(abs(rn*(1-2*xn)))
if lex>0: t.pencolor(0,0,min(int(lex*tico),255))
else: t.pencolor(max(255+int(lex*tico),0),max(255+int(lex*tico),0),0)
t.dot(size=1); t.update()
if x==-swid//2+2:
endt=dt()
print(f'Estimated total time: {(endt-startt)*(swid-5)} secs')
#Example: lyapunov(2,2.0,4.0,2.0,4.0,10000,100)
I attempted to use yield but I couldn't figure out where it should go.
On my slower machine, I was only able to test with a tiny N (e.g. 10) but I was able to speed up the code about 350 times. (Though this will be clearly lower as N increases.) There are two problems with your use of update(). The first is you call it too often -- you should outdent it from the y loop to the x loop so it's only called once on each vertical pass. Second, the dot() operator forces an automatic update() so you get no advantage from using tracer(). Replace dot() with some other method of drawing a pixel and you'll get back the advantage of using tracer() and update(). (As long as you move update() out of innermost loop as I noted.)
My rework of your code where I tried out these, and other, changes:
from turtle import Screen, Turtle
from math import log
from timeit import default_timer
def lyapunov(seq, xmin, xmax, ymin, ymax, N, tico):
xdif = xmax - xmin
ydif = ymax - ymin
truseq = str(bin(seq))[2:]
for x in range(2 - swid_2, swid_2 - 2):
if x == 2 - swid_2:
startt = default_timer()
tx = x * xdif / swid + xdif/2
for y in range(11 - shei_2, shei_2 - 1):
ty = y * ydif / shei + ydif/2
lex = 0
xn = prevxn = 0.5
for n in range(1, N+1):
rn = tx if truseq[n % len(truseq)] == '0' else ty
xn = rn * prevxn * (1 - prevxn)
prevxn = xn
if xn != 1:
lex += 1/N * log(abs(rn * (1 - xn*2)))
if lex > 0:
turtle.pencolor(0, 0, min(int(lex * tico), 255))
else:
lex_tico = max(int(lex * tico) + 255, 0)
turtle.pencolor(lex_tico, lex_tico, 0)
turtle.goto(x, y)
turtle.pendown()
turtle.penup()
screen.update()
if x == 2 - swid_2:
endt = default_timer()
print(f'Estimated total time: {(endt - startt) * (swid - 5)} secs')
screen = Screen()
screen.setup(2000, 1000, startx=0)
screen.bgcolor('pink')
screen.colormode(255)
screen.tracer(False)
swid = screen.window_width()
shei = screen.window_height()
swid_2 = swid//2
shei_2 = shei//2
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
turtle.setheading(90)
lyapunov(2, 2.0, 4.0, 2.0, 4.0, 10, 100)
screen.exitonclick()

swift: what does arc4andom_uniform() means

This might be a lame question and even have already answered in SO.i have even searched about this but could not understand in a proper way. what is happening here..??please help me out to understand this.
let size = Double(arc4random_uniform(5)) + 1
for index in 0..<ITEM_COUNT
{
let y = Double(arc4random_uniform(100)) + 50.0
let size = Double(arc4random_uniform(5)) + 1
entries.append(ChartEntry(x: Double(index) + 0.5, y: y, size: CGFloat(size)))
}
arc4random_uniform(x) returns a random value between 0 and x-1
Examples:
arc4random_uniform(2) -> returns 0 or 1 randomly
arc4random_uniform(2) == 0 returns true or false randomly
arc4random_uniform(6) + 1 returns a number between 1 and 6 (like a dice roll)
There are a multitude of reasons that arc4random_uniform(5) returns a number between 0 and 5, but the main one is that this is a basic functionality in programming, where numbers start at zero. An example of why this would be useful would be returning a random value from an array. Example:
func randomArrayValue(array: [Int]) -> Int {
let index = arc4random_uniform(array.count)
return array[index]
}
let arrayOfInt = [10,20,30]
print("Random Int: \(randomArrayValue(array: arrayOfInt))")
//"Random Int: 10"
//"Random Int: 20"
//"Random Int: 30"
For these three lines of code in your questions:
let y = Double(arc4random_uniform(100)) + 50.0
let size = Double(arc4random_uniform(5)) + 1
entries.append(ChartEntry(x: Double(index) + 0.5, y: y, size: CGFloat(size)))
y is a random variable between 50 and 149
size is a random variable between 1 and 5
you then add an item onto an array that goes onto a chart. The value being added specifies the x location (the index) and the y location (the random y value). Size is some code specific requirement, which we wouldn't be able to help with without seeing the functionality.

How do I iterate code over objects in a table in lua?

I am trying to write a code that does paralax scrolling. All the sprites in the "pieces" table need to shoot up into the air, then have their position reset to y=200 and x=randomnumber. Later on, I'm going to make them smaller or bigger depending on how fast they're moving for a true 3D effect.
Here's a naive attempt:
local speed = math.random(250,1000)
pieces = { "sprite", "sprite1", "sprite2", "sprite3", "sprite4",
"sprite5", "sprite6", "sprite7", "sprite8", "sprite9", "sprite10",
"sprite11", "sprite12", "sprite13", "sprite14", "sprite15" }
function update(self, dt)
for i, v in ipairs(pieces) do
p = go.get_position(v)
p.y = p.y + speed * dt
print(v)
if p.y > 800 then
p.y = -200
p.x = math.random(1,25) * math.random(10,35)*2
local speed = math.random(250,1000)
end
go.set_position(v)
end
end
If i print "v" inside the ipairs i get the desired output (sprite, sprite1, sprite, etc), so I thought that what I have above would work. It doesn't. Any help?
Here's the correct code which I figured out. If anyone sees this and needs it explaining, let me know.
local speed = math.random(250,1000)
--pieces = { "sprite1", "sprite2", "sprite3", "sprite4", "sprite5", "sprite6", "sprite7", "sprite8", "sprite9", "sprite10", "sprite11", "sprite12", "sprite13", "sprite14", "sprite15" }
pieces = { "go1", "go2", "go3" }
function update(self, dt)
for i, v in ipairs(pieces) do
p = go.get_position(v)
p.y = p.y + speed * dt
if p.y > 800 then
p.y = math.random(200,800)/-1
p.x = math.random(1,25) * math.random(10,35) * 2
local speed = math.random(250,1000)
go.set_position(p, v)
else
go.set_position(p, v)
end
end
end

Game Dev - Having trouble with AABB collision detection during jumps

My collision detection works by getting intersection between rectangles and reversing the effect. This is happening during each frame. It works great except in the case where the player is sitting on top of a corner and jumps. every once in a while the vertical intersection is greater than the horizontal intersection which makes my player slide down the side of the platform. Any suggestions?
-- detect initial collision
if mathy.hasCollided(player, platform) then
local playerBoundaries = player:boundaries()
-- list of intersections between platform and player
local bottomBoundary = mathy.bottomBoundary( playerBoundaries, platform )
local topBoundary = mathy.topBoundary( playerBoundaries, platform )
local rightBoundary = mathy.rightBoundary( playerBoundaries, platform )
local leftBoundary = mathy.leftBoundary( playerBoundaries, platform )
local smallestDist = ""
local smallestBoundary
local boundaries = {
bottom = bottomBoundary,
top = topBoundary,
right = rightBoundary,
left = leftBoundary
}
-- get the smallest intersection (thats the side we're probably closest to)
for direction, boundary in pairs(boundaries) do
if not smallestBoundary then
smallestBoundary = boundary
smallestDist = direction
end
if smallestBoundary > boundary then
smallestBoundary = boundary
smallestDist = direction
end
end
-- reverse effects depending on collision location
if smallestDist == "bottom" then
player.desiredPos:add(diffX, -bottomBoundary)
player.velocity.y = 0
player.onGround = true
elseif smallestDist == "top" then
player.velocity.y = 250
player.desiredPos:add(0, topBoundary)
elseif smallestDist == "right" then
player.desiredPos:add(-rightBoundary, 0)
elseif smallestDist == "left" then
player.desiredPos:add(leftBoundary, 0)
end
end
Its hard to tell from the short clip, but I think the issue is a result of checking for the smallest intersection instead of checking for the intersection in the direction of the object's velocity. You could try something like this in place of the smallestBoundary loop:
local boundary = nil
if (player.velocity.y > 0) then
boundary = topBoundary
elseif (player.velocity.y < 0) then
boundary = bottomBoundary
elseif (player.velocity.x > 0) then
boundary = rightBoundary
elseif (player.velocity.x < 0) then
boundary = leftBoundary
end
Of course, this isn't as robust as it can be. You might also try to combine the two approaches and do something like this in place of the smallestBoundary loop:
local yMod = math.abs(player.velocity.y)
local xMod = math.abs(player.velocity.x)
local topMod = player.velocity.y > 0 and yMod or 1
local bottomMod = player.velocity.y < 0 and yMod or 1
local rightMod = player.velocity.x > 0 and xMod or 1
local leftMod = player.velocity.x < 0 and xMod or 1
local boundaries = {
bottom = (bottomMod / MAX_VELOCITY) * bottomBoundary,
top = (topMod / MAX_VELOCITY) * topBoundary,
right = (rightMod / MAX_VELOCITY) * rightBoundary,
left = (leftMod / MAX_VELOCITY) * leftBoundary
}
for direction, boundary in pairs(boundaries) do
if not smallestBoundary then
smallestBoundary = boundary
smallestDist = direction
end
if smallestBoundary > boundary then
smallestBoundary = boundary
smallestDist = direction
end
end
This will do exactly what you're doing now, but will adjust the size of the boundary (in context of the comparison only) by the velocity of the player in that direction. So if you're moving in x with -5 and in y with -10, downward collisions in the y plane will have twice the weight of leftward collisions in the x plane. Of course, there's always the other option of adjusting the player in every plane of collision:
if bottomBoundary > 0 then
player.desiredPos:add(diffX, -bottomBoundary)
player.velocity.y = 0
player.onGround = true
end
if topBoundary > 0 then
player.velocity.y = 250
player.desiredPos:add(0, topBoundary)
end
if rightBoundary > 0 then
player.desiredPos:add(-rightBoundary, 0)
end
if leftBoundary > 0 then
player.desiredPos:add(leftBoundary, 0)
end
This last method would make the most sense, except you don't seem to handle collisions in all directions uniformly, for whatever reason, so it might not fit your architecture.
Keep in mind that I'm not familiar with the framework you're using, so this code might not work out of the box. Also, this post assumes that +y is up, -y is down, +x is right, and -x is left.

Lua Separation Steering algorithm groups overlapping rooms into one corner

I'm trying to implement a dungeon generation algorithm (presented here and demo-ed here ) that involves generating a random number of cells that overlap each other. The cells then are pushed apart/separated and then connected. Now, the original poster/author described that he is using a Separation Steering Algorithm in order to uniformly distribute the cells over an area. I haven't had much experience with flocking algorithm and/or separation steering behavior, thus I turned to google for an explanation (and found this ). My implementation (based on the article last mentioned) is as follows:
function pdg:_computeSeparation(_agent)
local neighbours = 0
local rtWidth = #self._rooms
local v =
{
x = self._rooms[_agent].startX,
y = self._rooms[_agent].startY,
--velocity = 1,
}
for i = 1, rtWidth do
if _agent ~= i then
local distance = math.dist(self._rooms[_agent].startX,
self._rooms[_agent].startY,
self._rooms[i].startX,
self._rooms[i].startY)
if distance < 12 then
--print("Separating agent: ".._agent.." from agent: "..i.."")
v.x = (v.x + self._rooms[_agent].startX - self._rooms[i].startX) * distance
v.y = (v.y + self._rooms[_agent].startY - self._rooms[i].startY) * distance
neighbours = neighbours + 1
end
end
end
if neighbours == 0 then
return v
else
v.x = v.x / neighbours
v.y = v.y / neighbours
v.x = v.x * -1
v.y = v.y * -1
pdg:_normalize(v, 1)
return v
end
end
self._rooms is a table that contains the original X and Y position of the Room in the grid, along with it's width and height (endX, endY).
The problem is that, instead of tiddly arranging the cells on the grid, it takes the overlapping cells and moves them into an area that goes from 1,1 to distance+2, distance+2 (as seen in my video [youtube])
I'm trying to understand why this is happening.
In case it's needed, here I parse the grid table, separate and fill the cells after the separation:
function pdg:separate( )
if #self._rooms > 0 then
--print("NR ROOMS: "..#self._rooms.."")
-- reset the map to empty
for x = 1, self._pdgMapWidth do
for y = 1, self._pdgMapHeight do
self._pdgMap[x][y] = 4
end
end
-- now, we separate the rooms
local numRooms = #self._rooms
for i = 1, numRooms do
local v = pdg:_computeSeparation(i)
--we adjust the x and y positions of the items in the room table
self._rooms[i].startX = v.x
self._rooms[i].startY = v.y
--self._rooms[i].endX = v.x + self._rooms[i].endX
--self._rooms[i].endY = v.y + self._rooms[i].endY
end
-- we render them again
for i = 1, numRooms do
local px = math.abs( math.floor(self._rooms[i].startX) )
local py = math.abs( math.floor(self._rooms[i].startY) )
for k = self.rectMinWidth, self._rooms[i].endX do
for v = self.rectMinHeight, self._rooms[i].endY do
print("PX IS AT: "..px.." and k is: "..k.." and their sum is: "..px+k.."")
print("PY IS AT: "..py.." and v is: "..v.." and their sum is: "..py+v.."")
if k == self.rectMinWidth or
v == self.rectMinHeight or
k == self._rooms[i].endX or
v == self._rooms[i].endY then
self._pdgMap[px+k][py+v] = 1
else
self._pdgMap[px+k][py+v] = 2
end
end
end
end
end
I have implemented this generation algorithm as well, and I came across more or less the same issue. All of my rectangles ended up in the topleft corner.
My problem was that I was normalizing velocity vectors with zero length. If you normalize those, you divide by zero, resulting in NaN.
You can fix this by simply performing a check whether your velocity's length is zero before using it in any further calculations.
I hope this helps!
Uhm I know it's an old question, but I noticed something and maybe it can be useful to somebody, so...
I think there's a problem here:
v.x = (v.x + self._rooms[_agent].startX - self._rooms[i].startX) * distance
v.y = (v.y + self._rooms[_agent].startY - self._rooms[i].startY) * distance
Why do you multiply these equations by the distance?
"(self._rooms[_agent].startX - self._rooms[i].startX)" already contains the (squared) distance!
Plus, multiplying everything by "distance" you modify your previous results stored in v!
If at least you put the "v.x" outside the bracket, the result would just be higher, the normalize function will fix it. Although that's some useless calculation...
By the way I'm pretty sure the code should be like:
v.x = v.x + (self._rooms[_agent].startX - self._rooms[i].startX)
v.y = v.y + (self._rooms[_agent].startY - self._rooms[i].startY)
I'll make an example. Imagine you have your main agent in (0,0) and three neighbours in (0,-2), (-2,0) and (0,2). A separation steering behaviour would move the main agent toward the X axis, at a normalized direction of (1,0).
Let's focus only on the Y component of the result vector.
The math should be something like this:
--Iteration 1
v.y = 0 + ( 0 + 2 )
--Iteration 2
v.y = 2 + ( 0 - 0 )
--Iteration 3
v.y = 2 + ( 0 - 2 )
--Result
v.y = 0
Which is consistent with our theory.
This is what your code do:
(note that the distance is always 2)
--Iteration 1
v.y = ( 0 + 0 + 2 ) * 2
--Iteration 2
v.y = ( 4 + 0 - 0 ) * 2
--Iteration 3
v.y = ( 8 + 0 - 2 ) * 2
--Result
v.y = 12
And if I got the separation steering behaviour right this can't be correct.

Resources