Here is my code: (just so you know I am a beginner and I just started this week, though I do have knowledge with other languages and game engines)
func _on_Area2D_area_entered(area):
get_parent().get_node("Level 1/Area2D/Flag").rotation_degrees += 1
What I was trying to accomplish was that the Player GameObject would see if its in the area of the Flag, and if it is, the flag would rotate.
I am not sure where the issue is. I think it is probably in the second line. I provided a screenshot below if I did the setup wrong. I have looked at the other close questions asked on the same topic, but they did not answer my question.
The "Player" GameObject is the one with the script that contains the detection if its in the area2D.
If you want to check if the Area2D is positioned correctly during runtime enable Debug -> Visible Collision Shapes.
If you want to check if _on_Area2D_area_entered is running, add breakpoints (or use print).
Did you get an error?
If there isn't a Node there, this expression will cause an error in runtime:
get_parent().get_node("Level 1/Area2D/Flag")
If you want to be able to check, you can use get_node_or_null and is_instance_valid.
Since you didn't mention any error, I'm going to guess the method is not running.
If the method is not running, the most likely culprit is that - I'm guessing given then name of the method - you connected the "area_entered" signal but intended to connect the "body_entered" signal.
The "area_entered" signal will trigger when another Area2D enters the Area2D. But I only see one Area2D in your scene tree. On the other hand the "body_entered" will trigger when a PhysicsBody2D (e.g StaticBody2D, KinematicBody2D, RigidBody2D) enters the Area2D. In either case you get what entered as a parameter of the method.
Other reasons why the Area2D might not be detecting what you want include no intersection of collision_layer and collision_mask and monitoring being disabled.
And to dismiss a couple possible misconceptions:
The "area_entered" and "body_entered" trigger when the Area2D or PhysicsBody2D respectively enter the Area2D, not every frame they are inside. So rotation_degrees += 1 is not a rotation animation.
You will get notifications of anything that trigger the signals, not just the object to which you connected it. You may have to further filter, e.g. if body == self:.
For people arriving here from search, I want to link a similar case: Enemy is not affected by bullets. And also my full explanation of how to set up physic nodes.
Related
Is it possible to check if a custom shape has been clicked. For instance, how would I check if a piano key, which is not a regular shape, has been clicked. I would assume that you would make an area 2D and make a Collision Polygon 2D, which has your shape, as a child, Then you would send a signal to a script if their was input, and the script would check if it was a click and respond accordingly. I tried this but it is not working. Below is my code. Any help is much appreciated!
func _on_Collision_input_event(viewport, event, shape_idx):
if event is InputEventMouseButton:
if event.is_pressed:
$"RadioCPressed".show()
print("Object Clicked")
else:
$"RadioCPressed".hide()
You have a typo, it should be event.is_pressed() not event.is_pressed. It must be giving you an error on run time.
Assuming it is not giving you an error, it probably means the method is not running at all (You can use a print or breakpoint at the start of the method to confirm). Also, double check you connected the signal. And make sure the Area has input_pickable set to true.
Something else you need to consider if whether or not there is something else getting the input. A common case is that people will use a ColorRect or TextureRect for background, and leave it with mouse_filter set to Stop (which is the default). Even if it is in the background, you need to set mouse_filter to Ignore, because Controls take priority over other nodes for input. See Using InputEvent.
I'm new to Phaser3 and before starting a crazy project, I want to know how I should start, switch between scenes. I saw that there are several functions, start, launch, switch, run, resume, pause, etc.
Example, lets say I want to have 2 scenes, a Menu and a Game. I boot on the Menu and I want to go to the Game scene and if I click on a button then come back to the Menu scene.
I've achieved this by calling the start function, but I noticed that the all, init, preload and create functions are called every time and therefore I'm loading all the images, setting all the listener over and over again.
This seems wrong, should I be using the launch or switch functions and pausing and resuming? But how do I hide the previous scene?
Thanks in advance.
This question might be a little too broad, but with Phaser 3 in mind, it still depends upon what purpose your menu serves.
I think most games have a main menu that will generally be called when the game first starts, and then won't be called again.
If this is an in-game menu, where settings can be changed or part of the game can be reset/restarted, then it might not make sense to redirect to a completely different scene.
With Phaser 3's support of multiple scenes - with Dev Log #119 and Dev Log #121 probably being the best current sources of information - another option would be to start a new scene within the current scene to handle this.
However, if this is really just UI, there's nothing to stop you from creating an overlay, instead of spawning an entire scene.
If you're concerned about performance I might think about whether the entire menu needs to be called, or if a simplified menu would work. Also, make sure that you're preloading assets before you're in the menu and main game.
I personally use Boot > Preloader > Splash Screen > Main Menu > Main Game scenes, where the Preloader loads the majority of the assets I'll need. This has the downside of a longer initial load, but the upside of minimal loading after this point.
Scene Transitions
How I handle these in my starter templates is to add the scenes to the Scene Manager when creating the scene. Then I transition by start to the first scene.
this.scene.add(Boot.Name, Boot);
this.scene.add(Preloader.Name, Preloader);
this.scene.add(SplashScreen.Name, SplashScreen);
this.scene.add(MainMenu.Name, MainMenu);
this.scene.start(Boot.Name);
Then I simply keep starting the next scenes as needed.
this.scene.start(Preloader.Name);
For another game that uses multiple scenes I ended up creating the following function (TypeScript) to handle this:
private sleepPreviousParallelScene(sceneToStart: string): Phaser.Scene {
if (this.uiSceneRunning !== sceneToStart) {
// Make sure that we properly handle the initial state, when no scene is set as running yet.
if (this.uiSceneRunning !== "") {
this.scene.get(this.uiSceneRunning).scene.sleep();
}
const newScene = this.scene.get(sceneToStart);
newScene.scene.start();
this.scene.bringToTop(sceneToStart);
this.uiSceneRunning = sceneToStart;
return newScene;
} else {
return this.scene.get(this.uiSceneRunning);
}
}
In the game I was using this for, I was trying to replicate a standard tab interface (like what's see in the Dev Logs above with the file folder-like interface).
Ok, here is the deal. In phaser 3, the start function actually SHUTS DOWN the previous scene as it starts the new one. This is why you see the init, preload ext every time. If however, you 'launch' your scenes instead, then they will not go through the whole shut down, restart sequence. You can move them to the top or bottom, set to receive input or not, etc. HOWEVER, BEWARE! As of phaser 3.16, sleep or pause of a scene does NOT shut down the update. As a result, if you launch a scene, then sleep it, it basically does nothing other than maybe set the flag, saying it's asleep even though it really isn't. So any update processing will continue in any launched scene. You can short circuit the update with a flag of some sort at the beginning, (that's what I've resorted to), but the other gotcha is the camera size. All the scenes must therefore have the same camera size or they will render (even if "sleeping") and mess up your display.
So, bottom line, it's going to be messy until sleep and pause actually work.
Ok I am a complete noob to pygame and programming in general. However I have been working on a game where the player falls and dodges objects. The idea is that the player remains within the top 1/4 of the screen and everything scrolls upwards to give the illusion of moving. This works fine however the code below is where I try and get another wall to spawn. This should work by deleting the sprite when it goes off the screen and spawning a new sprite when the image starts to go off the screen. The -2 is how far it should cross until another is spawned and my logic was to check the size of the group. While it only contained one it would iterate through the next section of code defining a new wall sprite identical to the old one but positioned below it. However this causes the program to crash probably because the old sprite is deleted causing an infinite loop. So if this is true how do you define a new sprite with the same attribute whilst not killing the other?
Also I realised that the code wont even work multiple times because I used "left_wall" which wont be defined after its been deleted so I tried using "wall" as I iterate through the group defining each sprite as wall in the for loop so every time a new wall is added it should be defined as wall so that I works infinitely. however I this error message AttributeError: 'Game' object has no attribute 'wall'. So at this moment I am stuck, not really knowing what to do next.
edit: I forgot to take off "self" when using "wall" which was the cause of the attribute error however it is still crashing like I mentioned in the first paragraph which is probably due to the old sprite being deleted.
sorry if this is confusing or not a properly structured question (like I said, I am a noob).
for wall in self.left_wall_sprite:
wall.rect.y -= self.player.vel.y
if wall.rect.bottom < 0: # move left wall
wall.kill()
if wall.rect.top == -2: # a new wall needs to be spawned
while len(self.left_wall_sprite) < 2:
self.new_left_wall = self.left_wall
self.new_left_wall.rect.top = self.left_wall.rect.bottom
self.all_sprites.add(self.new_left_wall)
self.left_wall_sprite.add(self.new_left_wall) # add the new sprite to the wall group so that it runs through the same cycle
With only this part of the code, it is hard to tell for sure what is going on - but what can be detected is that you create a new wall section, but do not "leave things as you found them" - so, that the code will still try to move the old wall.
If you really need to create a new sprite, go all the way: once the condition is met for the change:
get a reference to the old wall
remove the old wall from all groups (the .kill method)
create a new wall (create a new instance - you are just making an
assigment to an existing instance).
insert the new instance in all relevant groups.
As the code is, you are just pointing to the same wall object (self.left_wall) so that "self.let_wall" and "self.new_let_wall" are just references to the samething, and when you change a parameter (like rect.top) in one side, you can see the same change in the other.
Without the code for the class of your wall objects, one can't now if everything is set for correctly creating a new instance, but that could typically be done by self.left_wall = Wall((wall.rect.bottom, wall.rect.left, wall.rect.width, wall.rect.height)) (if your class's __init__ will take a rect as first parameter). This replaces the old self.rect_wall (which should have a reference on the wall variable yet) - you then just add it to the new groups.
But it looks like you can just reuse the same sprite - so, jsut update the top coordinate as you are doing -no need to fiddle with group containment or anythng:
if wall.rect.bottom <= 0:
wall.rect.top = wall.rect.bottom # or "0" at once
I'm working in a legacy application using MFC.
We have a mechanism to enable/disable controls depending on some business logic.
This mechanism is implemented in the CView-derived class. The way it works is all the views in the application derived from a common CView-derived class (CBaseView) and on the PreTranslateMessage all controls of the view are enabled/disabled.
This worked fine so far because all controls send at least WM_PAINT message when they need to be painted. So the system worked without the user having to move the mouse or anything. I recently added some drawing features and I had to use WS_EX_COMPOSITE to get ride of some flickering. With this flag activated my CView-derived class is not getting any called to PreTranslateMessage when creating the view....so the controls are not disabled until the user moves the mouse over the control.
I understand there is no way to send WM_PAINT using WS_EX_COMPOSITE but is there other message I can use to get the same behaviour???
Edited:
I am currently using the OnIdle approach but it has a big drawback, the windows doesn't become idle until after drawing all the controls...so when you enter the screen al controls are enabled and inmediately they are disabled...this makes a quite ugly effect!
More solutions???
Thanks in advance...
The logical place to enable/disable controls would be CView::OnUpdate, it is called by the framework after the view's document has been modified and from OnInitialUpdate(); you can also call this function if there is some change that would trigger re-evaluation of your business logic.
EDIT
After reading the question a bit more closely, what you could also do is to post a private message at the end of OnInitialUpdate and "catch" it in your PreTranslateMessage:
PostMessage(WM_APP, 0, 0);
Calling InvalidateRect followed by UpdateWindow against the window in question will mark the entire client area as dirty and force an immediate repaint. Remember that WM_PAINT is not really a message, in the queue in the usual sense, it is pushed out after all other messages have been processed for that window, which would include any invalidations of the area being drawn. No message is generated at all if there are no invalid segments of the active window display.
I just started maintaining a set of embedded Python plugins for a Qt application. I'm also new to both PyQt and Python, so bear with me.
I have an implementation of a QTreeWidget in one dialog where the "expanded" signal is not being caught by the corresponding handler. I have another dialog where it works just fine.
In the problem dialog, I can verify that the connect was successful.
connected = wdg.connect(wdg.treeView_,SIGNAL("expanded(QTreeViewItem*)"), wdg.expanded)
evaluates to True. When I click on the child indicators to expand an item, the [+] signs change to a minus, but nothing else happens. Likewise, when I click on the [-], it toggles back to [+]. I have set the ChildIndicatorPolicy to initially set the indicator to SHOW in both cases.
In the dialog that works OK, when the user clicks on the plus sign, the 'expanded' handler is executed. Only the indicator toggles when it is clicked.
My handler code is simply:
def expanded(self, item):
logging.debug("In expanded handler")
I have a breakpoint at the logging call, but the statement is never reached.
I the failing dialog, I have another signal that's connected immediately before the one above, and it works just fine:
wdg.connect(wdg.treeView_,SIGNAL("currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)"), wdg.itemChanged)
Is there anything that could prevent an expanded signal from firing or being caught? Another event perhaps? What should I be looking for? I realize that my nomenclature may be a bit off, and I welcome any correction in that regard as well.
The signal for a QTreeView expansion event is "expanded(QModelIndex)".
Alternatively, consider using the new style signal/slot syntax. I find it much easier than looking up the exact argument type for a particular signal.
wdg.treeView_.expanded.connect(wdg.expanded)
I found the problem. Despite the misleading name, the object is a QTreeWidge. When I replaced the name of the signal from "expanded" to "itemExpanded" and changed the parameter type to a QTreeWidgetItem*, everything worked.
wdg.connect(wdg.treeView_, SIGNAL("itemExpanded(QTreeWidgetItem*)"), wdg.expanded)