How to Stop an image from going through another image? - object

I am creating a game(Bomber Man), i need my character to not go through the wall blocks which are separate images in a control array. When i use this code the player goes through the walls and goes crazy in all directions.
*NOTE:I have already found out how to do collision detection, what my main question is what to do when collision is detected?
This is the code i have so far,
'imgWhite is my player,imgWall is my block of wall.
'JUMPW is my Const variable which is set to 325
Private Sub tmrBlock_Timer()
Dim x As Integer
For x = 0 To 72
If imgWhite.Left < (imgWall(x).Left + imgWall(x).Width) Then
white.dx = JUMPW
End If
If imgWall(x).Left < (imgWhite.Left + imgWhite.Width) Then
white.dx = -JUMPW
End If
If imgWhite.Top < (imgWall(x).Top + imgWall(x).Height) Then
white.dy = JUMPW
End If
If (imgWhite.Top + imgWhite.Height) > imgWall(x).Top Then
white.dy = -JUMPW
End If
Next x
End Sub

Perhaps a better approach is to check for collision in your player chatacter movement routine?
It looks like what you are doing here is if the character is in the wall, throw them back out (by JumpW). It may be better to say that the PC can only move in a direction in the first place if there is no impediment.
Also, you should probably either use for x = imgwall.LBOUND to imgwall.UBOUND or for each wall in imgwall loop, to avoid having to make code changes every time you add (or remove) a wall.

Related

Get Rigidbody2D collision velocity without the sliding factor

I have a flying car that I want to lose some HP when colliding with objects.
I connected car's RigidBody2D to this function to do that
func _on_Car_body_entered(body):
var force = linear_velocity.length()
var dmg = pow(force / 100, 2) - 0.25
if dmg <= 0: return
Health = Health - dmg
Now, since I don't have to be precise I'm just using current velocity as the force, though this is up for change.
After getting my 'force of impact', I put it into damage calculating formula and if damage is above 0, decrease HP by damage.
This works fine in most cases
BUT
I noticed that if car's going fast horizontally and just barely touch the ground (that's perfectly horizontal), car gets hit with a lot of damage, because I'm using the length of the velocity vector.
Ofcourse, this case can be managed by using just the Y component of the velocity vector, but then it removes any horizontal collisions, and vice versa, and it also leads me on to the path of programming vertical and horizontal collisions, and ofcourse those are not the only 2 directions of colisions I need.
Is there a way to remove the sliding factor from this equation?
You can get the sine of the angle between your velocity and the collision normal, and then take the absolute of that.
# 0 When sliding along the wall. 1 when hitting the wall head on
var slide_factor = abs(cos(vel_last_frame.angle_to(collision_normal)))
This will give you a value from 0 to 1. When you are just sliding along the wall, this value will be 0, and when you hit the wall straight on, it will be 1.
I am using the velocity from the last frame here so that it gets the velocity just before the collision. I get it by setting vel_last_frame to linear_velocity inside the _physics_process function.
You can only get the collision normal inside the _integrate_forces function using PhysicsDirectBodyState.get_local_contact_normal(), so you need to make a variable that can be accessed in this function and the _on_Car_body_entered function. Note that you need to set contact_monitor to true and contacts_reported to at least 1 for this function to work.
var collision_normal
func _integrate_forces(state):
# Check if there is a collision
if state.get_contact_count():
# contact_monitor must be true and contacts_reported must be at least 1 for this to work
collision_normal = state.get_contact_local_normal(0)
Now in the _on_Car_body_entered_function, you can multiply dmg by sliding_factor to scale it less depending on how much you are sliding against the wall.
func _on_Car_body_entered(body):
var force = linear_velocity.length()
# 0 When sliding along the wall. 1 when hitting the wall head on
var slide_factor = abs(cos(vel_last_frame.angle_to(collision_normal)))
var dmg = pow(force / 100, 2) - 0.25
# Reduce dmg depending on how much you are sliding against the wall
dmg *= slide_factor
if dmg <= 0: return
Health = Health - dmg
Found a solution for my problem here
This gives me a predictable force range to work with.
I copied all code for 2D collision, just added damage calculation
Range of forces my objects produce is <3000 for small collisions like scratches and bumps, ~10k for beginner friendly damage, and 20k+ for when I really slam the gas pedal, so I just convert that force to damage that I want.
Best part is that I don't have to use the body_entered from RigidBody2D, because now all my cars have this calculation in them, so when 2 of them collide they both get damaged.
extends RigidBody2D
var collision_force : Vector2 = Vector2.ZERO
var previous_linear_velocity : Vector2 = Vector2.ZERO
func _integrate_forces(state : Physics2DDirectBodyState)->void:
collision_force = Vector2.ZERO
if state.get_contact_count() > 0:
var dv : Vector2 = state.linear_velocity - previous_linear_velocity
collision_force = dv / (state.inverse_mass * state.step)
var dmg = collision_force.length() / 2000 - 2
if dmg > 0:
set_hp(Health - dmg)
emit_signal("Damaged")
previous_linear_velocity = state.linear_velocity
**OLD ANSWER**
RUBBER DUCK HERE
In script for car I added a new variable var last_linear_velocity = Vector2()
Then stored the last velocity in _process
func _process(delta):
last_linear_velocity = linear_velocity
Not in _integrate_forces because if I put it there then the new and last velocities are the same.
And just changed how force is calculated in the function mentioned above, so it looks like this
func _on_Car_body_entered(body):
var force = last_linear_velocity.length() - linear_velocity.length()
var dmg = pow(force / 100, 2) - 0.25
if dmg <= 0: return
Health = Health - dmg
Now I get a nice predicable range of values and can transform that to damage.
NOTE
I noticed that sometimes when collision occures the difference between the last and current velocity lengths is negative, as in - car is accelerating.
Anyway, this works for me for now.
If you find a better solutions do post it, as I couldn't find a solution to this problem online elswhere.

The rocks refuse to split properly

Basically, when the player collides with a rock, the rock should split into smaller rocks. Instead the rock creates a new rock that is smaller, but then becomes a bigger rock, why is it doing this?
func split():
if scale.length() > 1:
for i in range(1, 2):
var offset = Vector2(cos(i * PI / 4), sin(i * PI / 4))
var child = rock.instance()
child.scale = scale/4
child.position = position + offset * scale
child.vel = vel + offset
get_parent().add_child(child)
get_parent().remove_child(self)
Full source here
Edit: I found a solution. When I originally posted this I was using _ready() to set the properties of new rocks, I didn't realize that that function isn't called when creating a new instance, so when I created a child instance, these properties weren't set before I was changing them, but after when I added the children to the parent scene. So to fix it I simply changed _ready to _init and it solved the problem.
Ranges are inclusive on the lower end and exclusive on the higher end.
So doing for i in range(1, 2) makes your loop have only one iteration.

Pygame for loop iterates once, and then does not iterate again

Essentially, what I'm doing is taking the enemies list (on line 1), which is holding a list of coordinates and iterate through each pair in the enemies list at the bottom.
I want to go through each enemy in the list, get the y coordinate, add 10 and then go to the next enemy and add 10, so on and so forth. For some reason, it adds 10 ONCE and then stops, and the enemies do not fall down the screen. I don't know why this is happening. Why is it not running through the for loop anymore? Thank you so much for any help.
NOTE: I removed some code at the top for the sake of being less confusing. The update() function is just the pygame flip function.
enemies = [[100,0], [150,0]]
while True:
for enemy in enemies:
x = enemy[0]
y = enemy[1]
y += 10
pygame.draw.rect(screen, (255,0,0), (x, y,10,10))
# uses flip to update the screen
update()
# FPS
clock.tick(20)
You're trying to modify a local variable, not the value in the list. You need to write:
enemy[1] += 10
Since integers are immutable (they cannot be changed), the line y = enemy[1] can be thought of as "copy the value from enemy[1] into y".

SnapSVG rotation issue after first rotation

I have created a small demo in this codepen.
When clicking any of the four buttons, the object is rotated to the correct position using the following...
function rotateTo(angle) {
var ring = paper.select("#INNER_RING");
var bbox = ring.getBBox();
var cx = bbox.cx;
var cy = bbox.cy;
var trans = 'r' + angle + ' ' + cx + ' ' + cy; // + 's1,1,' + cx + ',' + cy;
console.log(trans);
ring.stop().animate({
transform: trans
},
750,
mina.linear,
function() {
settings.currentRotation = angle;
});
}
On the first rotation, everything goes great. However, subsequent rotations not only rotate but appear to scale as the rotation takes place. The object does end up in the correct final position, but the scaling is not wanted.
What could be happening as part of the first transformation that would cause subsequent ones to do this?
Animating matrices from a previous matrix typically gets pretty messy, as they often don't do things that we think they do (I asked a previous similar question in the past here which may be of use, getting your head around matrix animation if you wanted a deeper understanding). At the start of the first animation, there is no transform matrix to animate from, so it's all pretty straightforward.
However, at the end of the 1st animation, there is a transform matrix left there applying to the element, which causes a problem, as the next animation it tries to animate from that last matrix.
It isn't quite clear what you actually want to do, on a subsequent button press. Whether you want to rotate 'another' 90 degrees, or just rotate 90 from scratch again.
If you just want to repeat the animation, you could just reset the transform to scratch, which simply gets around the problem (but note, there may be an issue if you press the button mid animate).
ring.attr('transform','')
jsbin
If you want to do more complex things, and need to animate from a previous state, I would use a slightly different method of animate, and not animate on an element, but use Snap.animate and build your transform string each time. So the could could look more like this...
var rotateFrom = settings.currentRotation;
var rotateTo = rotateFrom + parseInt(angle)
Snap.animate(rotateFrom, rotateTo, function( val ) {
ring.transform( 'r' + val + ',' + cx + ',' + cy )
}, 750, mina.linear, function() {
settings.currentRotation = rotateTo;
});
jsbin2
Snap.animate() takes 2 initial arguments that are a starting value and an end value, and then interpolates between them, passing that as 'val' to the function which is passed in. The docs for that can be found here
This way, you don't care about animating from previous transforms (which are stored as matrices), and you control the real specifics of whats happening each animation loop.
You may just need to tweak it and store the rotation each time the func is called (rather than just when it finishes), depending on what you want to do mid-animation if the button is pressed before it's finished.

Shape.Top and Shape.Left Issues

I have some code that looks at the position of a line that a user can edit. The purpose of editing this line is to get rough measurements from a plot plan which works so far. I'm working on making it much more interactive by recording measured positions as well.
Here's the issue. When this shape (a connector elbow) is on the cabinet and the access point it records its position in points using shape.top and shape.left (code below). When I resize this line to put the access point end on another access point, the cabinet position changes as well even though it didn't move. I did notice that the scale height and width changed on the drawing but I can't figure out why that would affect the initial point.
It's worth noting that as you rotate the elbow connector the value of width and height rotate. That means sometimes height is up and down and sometimes its the value you'd expect width to be. Still the left position only stays constant when the connector is rotated 180 degrees.
Is there a relation between scale height/width and the top/left value?
Sub Measure()
Set sp = ActiveSheet.Shapes("Measurement")
Msgbox(sp.Top & "//" & sp.Left)
end sub
Edit: So I realize I mentioned a lot about the program, the real problem is why do the top left measurements change despite the top left staying stationary on the screen? And only in the 270/90 rotation (happens automatically depending how you drag the line)
Add Screen Shots (Measurements are Top then left)
This is the first screen shot with a 270 rotation
This is the second, notice the top left stayed stationary but the points changed
This next group is with a 180/0 rotation (shape auto rotates, otherwise I would just lock it and be done).
Rotated 180 first screen shot
Rotated 180 second screen shot
I sympathized with your problem recognition and I did not understand this phenomenon, but after testing, it seems that the original coordinates are preserved even if the figure is rotated. This is because even if the rotation is performed again, the rotation is performed by the original coordinate value. If you artificially align the position, the coordinates appear to move accordingly. I tested it as follows.
Sub Measure()
Dim rngT As Range
Dim sp As Shape
Set sp = ActiveSheet.Shapes("Measurement")
'MsgBox (sp.Top & "//" & sp.Left)
Set rngT = Range("k" & Rows.Count).End(xlUp).Offset(1, 0)
rngT = sp.Top
rngT.Offset(, 1) = sp.Left
End Sub
Sub test()
Dim sp As Shape
Dim Ws As Worksheet
Dim vDB
Dim i As Integer, t As Single, l As Single
Dim r As Integer
vDB = Range("k2", Range("L" & Rows.Count).End(xlUp))
Set Ws = ActiveSheet
r = UBound(vDB, 1)
For i = 1 To r
t = vDB(i, 1)
l = vDB(i, 2)
Set sp = Ws.Shapes.AddShape(msoShapeRectangle, l, t, 10, 10)
Next i
End Sub
I figured it out guys.
So in orientation 0 or 180 the Shape.top and Shape.Left give accurate consistent measurements.
In the 90 or 270 the top and left have to be adjusted to give accurate consistent measurements.
The code is Shape.Top-((Shape.Width-Shape.Height)/2) and Shape.Left+((Shape.Width-Shape.Height)/2)
(NOTE: THIS ADJUSTMENT ONLY APPLIES WHEN OBJECT IS ROTATED 90 or 270//Shape.Rotation)
Also, the shape used here is an elbow connector

Resources