12 Replies - 987 Views - Last Post: 23 March 2013 - 05:27 PM Rate Topic: -----

#1 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Noclip Glitch

Posted 22 March 2013 - 02:16 AM

This is technically an Android problem, but as the part where the issue is contains 100% Java, I figured that my question would get more exposure in this forum.

In this game-to-be, my character moves around obstacles. He can move in four directions, and he can move a maximum of two tiles at a time. When a path is blocked, the method for movement in that direction is blocked. If the blockage is two tiles away, then only the second tile is blocked, but if it's one tile away, then both tiles in that direction are blocked:

Posted Image

			for(StaticObstacle obstacle : staticObstacles)
			{
				//if the obstacle exists
				if(obstacle != null)
				{
					//if there's a collision
					if(Rect.intersects(obstacle.getRect(), action.getRect()))
					{
						//disallow movement
						action.setState(INACTIVE);
						//if the blockage is directly adjacent to the character
						if(Math.abs(action.getProximity()) == 1)
						{
							for(ActionSprite blockedaction : actionGrid)
							{
								//disallow all movement in that direction
								if(blockedaction.getDirection().equals(action.getDirection()))
									blockedaction.setState(INACTIVE);
							}															
						}						
					}					
				}
				else
					Log.d("Collision Detection", "staticObstacles(" + Integer.toString(_debug_) + ") = null");
			}



The character is able to move to any active "ActionSprite":

			int xTouch = (int)Math.round(event.getX());
			int yTouch = (int)Math.round(event.getY());
			
			for(ActionSprite action : actionGrid)
			{
				if(action.checkCollision(xTouch, yTouch))
				{
					Log.d( "NoClip", "ActionSprite -- " + action.getDirection() + "(" + action.getProximity() + ") = " + action.getActive() );
					if(action.getActive())
						hero.move(action.getX(), action.getY());
				}	
		
			}	



You might note that there's a LogCat.debug message in there as well. Imagine that's a System.err.println message. They're pretty much the same thing.

The code works perfectly if it's left or up from the character, but there's a Noclip glitch on the second tile right and down from the character. Here's the debug output from two action.getProximity() = 1 blockages:

Posted Image

//noclip glitch -- the second tile isn't blocked but the first is.
03-22 07:54:43.280: D/NoClip(4332): ActionSprite -- right(1) = false
03-22 07:54:44.602: D/NoClip(4332): ActionSprite -- right(2) = true
//working correctly, both tiles are blocked.
03-22 07:55:20.468: D/NoClip(4332): ActionSprite -- up(1) = false
03-22 07:55:21.822: D/NoClip(4332): ActionSprite -- up(2) = false



The code for an inactive ActionSprite is as follows:

	private enum State
	{
		attack, inactive, move, interact; 
	}
        //[...]
	public void setState(String state)
	{
		State currentState = State.valueOf(state);
		
		switch(currentState)
		{
			case attack:
				this.currentFrame = 0;
				this.active = true;
				break;
			case inactive:
				this.currentFrame = 1;
				this.active = false;
				break;
			case move:
				this.currentFrame = 2;
				this.active = true;
				break;
			case interact:
				this.currentFrame = 3;
				this.active = true;
				break;
		}
	}



If an ActionSprite is inactive, it's no longer painted to the screen (well, a blank image is painted). Here we see that the blank images are being painted in place of the blocked squares, but you can still move two squares down. Why does the currentFrame change to 1, but the value of this.active stay true? I have no clue. That's why I'm here.

Posted Image


It's a lot to put together, and that's part of the reason why I haven't been able to fix it yet. Directional blockages work in 2/4 directions, but there's nothing to suggest that it shouldn't work in all directions. I'm sure it's something silly that I'm doing. I just need to be pushed in the right direction. Something is happening to change this.active to true, but this.currentFrame retains its value of 1 (the sprite is a blank image)...

Is This A Good Question/Topic? 3
  • +

Replies To: Noclip Glitch

#2 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 22 March 2013 - 02:47 AM

I debugged the program some more and I found this:
								if(blockedaction.getDirection().equals(action.getDirection()))
								{
									blockedaction.setState(INACTIVE);
									if(blockedaction.getDirection().equals("down") || blockedaction.getDirection().equals("right"))
										Log.d("NoClip", blockedaction.getDirection() + " = " + blockedaction.getActive());									
								}



If I test whether or not those specific ActionSprites are active -- and remember that the character can't possibly move to an inactive ActionSprite -- both the ActionSprites in the far right and far down positions returned inactive if their closer counterparts were also inactive. Here's the output of the debugging:

//FORMAT: (String)direction = (boolean)active
03-22 09:38:58.888: D/NoClip(1136): right = false
03-22 09:38:59.029: D/NoClip(1136): down = false



And they remain false, they don't switch to true directly afterwards. I stood there for a few seconds and ran a few tests. However, when I test whether or not they're okay to move to:

					if(action.getActive())
						hero.move(action.getX(), action.getY());



the if(action.getActive()) check performs the action associated with true... Even though the program JUST told me that they are definitely false... This issue only occurs when I try to noclip to the right, or down. I'm so stumped.

Please, please, please help me. I'm just a poor, defenseless, undergraduate noob.

This post has been edited by Gungnir: 22 March 2013 - 02:56 AM

Was This Post Helpful? 0
  • +
  • -

#3 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 22 March 2013 - 03:03 AM

If I run the two LogCat.debug messages at the same time, at the time of the noclip I get this message:

//right(2) == false
03-22 09:59:44.678: D/NoClip(1354): right = false
//when touched, right(2) == true
03-22 09:59:44.748: E/NoClip(1354): ActionSprite -- right(2) = true
//after touch event, right(2) == false again
03-22 09:59:44.808: D/NoClip(1354): right = false



I'm pretty sure that the programming gods are punishing me for my sins, but any help would be appreciated.

The construction and instantiation of the ActionSprites are as follows:

	public ActionSprite(GameView view, Bitmap bmp, int posx, int posy) 
	{
		super(view, bmp);
		this.width = bmp.getWidth() / BMP_COLUMNS;
		this.height = bmp.getHeight() / BMP_ROWS;
		this.posx = posx;
		this.posy = posy;
		this.proximity = Math.abs((this.posx + this.posy));
		if(Math.abs(this.posx) > Math.abs(this.posy))
		{
			if(this.posx > 0)
				this.direction = "right";
			else
				this.direction = "left";
		}
		else 
		{
			if(this.posy > 0)
				this.direction = "down";
			else
				this.direction = "up";
		}
	}

		for(int ixl = -2; ixl < 0; ixl++)
			actionGrid.add(new ActionSprite(this, actionBMP, ixl, 0));
		for(int ixr = 1; ixr < 3; ixr++)
			actionGrid.add(new ActionSprite(this, actionBMP, ixr, 0));
		for(int iyl = -2; iyl < 0; iyl++)
			actionGrid.add(new ActionSprite(this, actionBMP, 0, iyl));
		for(int iyr = 1; iyr < 3; iyr++)
			actionGrid.add(new ActionSprite(this, actionBMP, 0, iyr));


This post has been edited by Gungnir: 22 March 2013 - 03:06 AM

Was This Post Helpful? 0
  • +
  • -

#4 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 856
  • View blog
  • Posts: 2,620
  • Joined: 29-July 11

Re: Noclip Glitch

Posted 22 March 2013 - 03:22 AM

This looks tile based. Why do collision detection like this? Why not just:

1,1,1,1,1,1,1
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,4,0,0

1 is a wall, 0 is the floor, and 4 is the player.

if(playerTile + 1 == 0) //the tile in front of the player.
    move();



The player can only move in tiles with 0 value.
Was This Post Helpful? 1
  • +
  • -

#5 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 22 March 2013 - 07:30 AM

Because I need my tile size to be dynamic due to the scaling systems used by the Android OS (ldpi, mdpi, hdpi, xhdpi, xxhdpi). The engine doesn't know how big a tile is, only individual classes know how big their tilesize is. Also, this way, I can move bad guys around by simulating touch events. There are just more possibilities when using dynamic rects. Android has its own Rect library.

My collision detection system isn't the issue. The issue here is the noclipping.

I fixed the issue.

Every frame, all states reset and reevaluate. The program is threaded. The issue was that the touch events were being evaluated between the time that the ActionSprite states are reset to active, and the time that they're reevaluated to inactive.

So something like:
//reset left
// > evaluate left
//reset right
// >> evaluate touch event
// > evaluate right
//reset up
// > evaluate up
//reset down
// >> evaluate touch event
// > evaluate down



I debugged it by querying the strings in the setState(String state) method. I got the following output:

03-22 14:19:50.649: E/NoClip(999): ActionSprite -- right(2) = move
03-22 14:20:17.252: E/NoClip(999): ActionSprite -- down(2) = move
03-22 14:20:23.332: E/NoClip(999): ActionSprite -- left(1) = inactive



As you can see, the right and down gave false positives because they had yet to be evaluated. I'm rewriting it now, but I'm pretty sure that I've solved the issue. Thank you for your help!

EDIT: Yep! That was the issue! I made the loops run faster and more efficiently, and the noclipping glitch is gone.

This post has been edited by Gungnir: 22 March 2013 - 08:37 AM

Was This Post Helpful? 0
  • +
  • -

#6 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1413
  • View blog
  • Posts: 3,135
  • Joined: 05-April 11

Re: Noclip Glitch

Posted 22 March 2013 - 10:12 AM

So it was actually a problem with your game loop
Was This Post Helpful? 3
  • +
  • -

#7 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 856
  • View blog
  • Posts: 2,620
  • Joined: 29-July 11

Re: Noclip Glitch

Posted 22 March 2013 - 02:45 PM

Sounds like a synchronization issue between the UI thread for input and the render/update thread.

Dynamic tile size? Eck. Sounds like a nightmare! I would just write code for the highest supported resolutions and scale downward match lower resolutions in my render(). Anyway, I am glad you got it worked out.
Was This Post Helpful? 1
  • +
  • -

#8 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 22 March 2013 - 09:12 PM

Farrell2k is correct. It was a synchronization issue. It just took too bloody long for three nested FOR loops to execute, so my touch events (clicks) were steamrolling through the middle of that execution.

I took out one of the FOR loops, had the second FOR loop write to a list. The list contained directions, which I would later check against my actionGrid in order to deactivate any blocked ActionSprites. It's pretty messy at the moment, but it works and I can tidy it up later.

Furthermore, I added another loop in the program to manually check for any noclip glitches. If the hero rect intersects an obstacle rect, the hero moves back to his previous position. It only takes one frame to execute, so the user doesn't even notice, but it means that I won't have any noclip glitches in the future.

This post has been edited by Gungnir: 22 March 2013 - 09:21 PM

Was This Post Helpful? 0
  • +
  • -

#9 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 856
  • View blog
  • Posts: 2,620
  • Joined: 29-July 11

Re: Noclip Glitch

Posted 23 March 2013 - 12:19 AM

Not to drag this on, but instead of hacking your way around problems like this, the preferred way to handle it is to marshall input events to the render/update thread and handle them there. You grab all of the InputEvents and store them in a list as they happen.

LinkedList<InputEvent> events = new LinkedList<InputEvent>();

//override in View.
public synchronized boolean onTouchEvent(MotionEvent e) {
    events.add(e);  /add the event to the list for processing later.
    return true;
}



Then in the game loop that executes in the update/render thread.

public void handleInput() {
    //get the events from list via a loop and process/remove them.
    InputEvent e = //get even from loop.
    if(e instanceof MouseEvent) {
        //process event as you see fit, then remove it from the list.
    }   
}



You will avoid any sync issues like this.

This post has been edited by farrell2k: 23 March 2013 - 01:39 AM

Was This Post Helpful? 1
  • +
  • -

#10 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 23 March 2013 - 09:33 AM

All feedback is greatly appreciated. I'm by no means an expert, and I'm always eager to learn from others who have more experience on the subject.

Unfortunately, the Android OS is very particular about how you can implement touch events. First of all, as it's a method of the View class, it can only be called by overriding the existing onTouchEvent method in a child class of View (a custom view). Apps have no control over when or how they're called, only what happens when they're called. I'll look into the subject further, but I'm fairly certain that the Android OS runs touch events as a separate user to any app, or at least as a part of the operating system that the [/inline]android.view.View.onTouchEvent[/inline] method is simply a conduit to.

As I said -- I'm no expert. I could be entirely wrong. This is my first Android game, and I'm just working from personal experience.

I'm glad that this noclipping business is all behind me. This is day 5 of development, and this glitch took up almost 2 whole days. It definitely kicked my ass, but I learned from the experience. Since this topic I've managed to implement two more sprite objects. This really isn't the place to log my progression, but... well, I can't think of an excuse, but I'm posting a screen anyway. :D

Posted Image
Was This Post Helpful? 4
  • +
  • -

#11 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 23 March 2013 - 09:41 AM

I've looked into it, and this was all I could find on queueing touch events for later use:

-- Stackoverflow > Not all touch events being received by Android view

Going to keep looking, because I think that this would benifit my app greatly. Thanks, Farrell2k!

Going to finally let this thread die out so that I don't push more important threads off of the first page.

This post has been edited by Gungnir: 23 March 2013 - 09:44 AM

Was This Post Helpful? 0
  • +
  • -

#12 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1413
  • View blog
  • Posts: 3,135
  • Joined: 05-April 11

Re: Noclip Glitch

Posted 23 March 2013 - 10:58 AM

This is the solution I could come up wit. I hope it works
By using the below code, you will have a list of events used in a game cycle, and another list which queues the GUI touch events up
It should, according to me, be a very efficient way and won't stop the GUI from being responsive, which otherwise will shut down your activity (hopefully this will never happen!)
private LinkedList<Point> tapEvents; //Contains all of the tap events for one cycle in the game loop

@Override
public void onTouchEvent(MotionEvent e) {
	//Add some logic to check if it is a tap on the screen
	if (tap)
		addTapEvent(e.getX(), e.getY());
}

private synchronized addTapEvent(int x, int y) {
	tapEvents.add(new Point(x, y))
}

public synchronized LinkedList<Point> getCycleEvents() {
	LinkedList<Point> temp = tapEvents; 
	tapEvents = new LinkedList<Point>(); //Reset this cycle, we will now add events to a new list for a new cycle
	return temp; //Return the events happening in this cycle
}


This post has been edited by CasiOo: 23 March 2013 - 11:08 AM

Was This Post Helpful? 2
  • +
  • -

#13 Gungnir  Icon User is offline

  • Your Imaginary Friend

Reputation: 152
  • View blog
  • Posts: 527
  • Joined: 21-May 11

Re: Noclip Glitch

Posted 23 March 2013 - 05:27 PM

Oh lord.

I was reading through CasiOo's snippet and I realised how silly I've been. Literally the only information that I need from the touch events is the (x, y) coordinate. If I feed that into a list, I can check the list wherever I want in my code, thus giving me more control over user input. There's been absolutely no reason for me to perform these logic checks when onTouchEvent() is called.

When snippets are working again, you should post this. I couldn't find anything comparable on Google.

You're a life saver, CasiOo! You too, Farrell2k.

EDIT: I'll implement this later, as I've forgotten to sleep.

ADDENDUM: It works! Obviously a few tiny adjustments had to be made, but for all intents and purposes the snippet that you posted works. I now have a greater control over user input. Thank you!

This post has been edited by Gungnir: 23 March 2013 - 11:53 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1