WorldViz User Forum  

Go Back   WorldViz User Forum > Vizard

Reply
 
Thread Tools Rating: Thread Rating: 10 votes, 5.00 average. Display Modes
  #1  
Old 12-16-2013, 06:24 AM
chris2307 chris2307 is offline
Member
 
Join Date: Nov 2013
Posts: 36
Unexpected Avatar lookAt() behavior when using yield statements

Hi,

I have an encountered some unexpected behavior in my Vizard application which I cannot work out. I should stress that I am relatively new to Python and the use of Yield statements. My problem may or may not be directly caused by the use of them.

The application I am creating uses two differing scenarios; the first scenario requires the user to follow an avatar and the second scenario requires the user to retrace the route that they went on when they followed the avatar.

I have created two classes (one for each scenario). Within the main.py file, I call a main functionbusing viztask.schedule(). I have used an (rather un-elegant) if statement that queries the state of the program which determines which scenario to run.

At the end of following the avatar, the visibility of the avatar is disabled. The scenario returns and the next scenario is played out where the user retraces the route. When this has finished, we re-initialise the following avatar scenario which includes placing the avatar back in the start position and setting the orientation to what ever direction it needs to go.

However, I call the lookAt() function to do this but it doesn't seem to take any effect. I used debug statements everywhere to try and find out why this wasn't working and found that actually, lookAt() was doing what it was supposed to do but at some other point, the avatar orientation was put back to where it was facing before calling the lookAt() function.

I am going to put some code down now. Sorry for the long explanation (finding this difficult to word!)

Code:
def runExperiment():
	
	while True:
		
		if(programState.isInitExperiment()):

			# Code related to setting up an experiment
			yield followAvatar.setUpExperiment(experiment.returnCurrentExperimentRoute(), experiment.returnFailureCoordinates())

			print "1a:",followAvatar.returnAvatarEuler()
			followAvatar.setAvatarEuler()
			print "1b:",followAvatar.returnAvatarEuler()
			
			programState.setTrainingInstructions()
			
			print "2a:",followAvatar.returnAvatarEuler()
			followAvatar.setAvatarEuler()
			print "2b:",followAvatar.returnAvatarEuler()
			
		elif(programState.isTrainingInstructions()):
			
			# Code related to displaying the introduction screen should go here
			yield message.displayMessage("Follow Avatar\nThis is a test message and not to be used in final version of the application")
			if(win32api.GetAsyncKeyState(win32con.VK_SPACE)):
				yield programState.setTrainingPhase()
				yield message.removeMessage()
		
		elif(programState.isTrainingPhase()):
			
			# Code related to the running the training phase should go here
			
			yield followAvatar.runExperiment()
			if (followAvatar.experimentEnded):
				yield programState.setInitTest()
				
		elif(programState.isInitTest()):
			
			# Code related to initialising the testing phase
			
			yield retraceRoute.setUpExperiment(experiment.returnCurrentExperimentRoute(), experiment.returnFailureCoordinates())
			yield programState.setTestInstructions()
				
		elif(programState.isTestInstructions()):
			
			# Code related to displaying the test instructions to go here
			
			yield message.displayMessage("Retrace the route\nThis is a test message and not to be used in the final version of the application")
			if(win32api.GetAsyncKeyState(win32con.VK_SPACE)):
				yield programState.setTestingPhase()
				yield message.removeMessage()
				#TODO - Somehow block movement
			
		elif(programState.isTestingPhase()):
			
			yield retraceRoute.runExperiment()
			
			if(retraceRoute.returnResult() == 1): 			# Passed Testing
				
				if (experiment.nextExperiment() == True): 	# Is there another experiment?
					yield programState.setInitExperiment() 	# Initiate that experiment
				else:										# If not...
					yield programState.setTestingComplete()	# Testing Complete
					
			elif(retraceRoute.returnResult() == 2): 		# Failed Testing
				
				if(experiment.experimentFailed() == True): 	# All tries used up?
					yield programState.setTestingComplete()	# Testing Complete
				else:										# If not...
					yield programState.setInitTest()		# Initiate the testing again

		elif(programState.isTestingComplete()):
			
			# Code to run once ALL testing have been completed to go here			
			print "Testing Complete"
			yield viz.quit()

viztask.schedule(runExperiment())
This is the bit I think is important:

Code:
		if(programState.isInitExperiment()):

		# Code related to setting up an experiment
		yield followAvatar.setUpExperiment(experiment.returnCurrentExperimentRoute(), experiment.returnFailureCoordinates())

		print "1a:",followAvatar.returnAvatarEuler()
		followAvatar.setAvatarEuler()
		print "1b:",followAvatar.returnAvatarEuler()
		
		programState.setTrainingInstructions()
		
		print "2a:",followAvatar.returnAvatarEuler()
		followAvatar.setAvatarEuler()
		print "2b:",followAvatar.returnAvatarEuler()
			
		elif(programState.isTrainingInstructions()):
			
		# Code related to displaying the introduction screen should go here
		yield message.displayMessage("Follow Avatar\nThis is a test message and not to be used in final version of the application")
		if(win32api.GetAsyncKeyState(win32con.VK_SPACE)):
			yield programState.setTrainingPhase()
			yield message.removeMessage()
Where you see the followAvatar.setUpExperiment() function called, we initialise the experiment and move the avatar to the start position and orientate it so it faces the direction of intended travel. (I'll paste this method below).

The next line prints out the avatar euler to the console. I've also identified this with a number so you can clearly see the output. When I first return the euler, it is not how I set it within the method. The euler has returned to (not how it was when first loaded in to Vizard at the very beginning) but how it was before I called the .lookAt() function.

So I then call a method to reset this. This method does nothing but call the lookAt() function on the avatar from within the same class. Then, returning the euler, I can see it is correct.

Where it gets really puzzling for me, I then set the state of application (sounds simple enough). If I put a yield statement in front of this line, I see the same behavior. The avatar oritentation returns to how it was in the previous experiment (ignoring the last two times I called .lookAt(). If I take out the yield statement, as shown in the code example, I do not see this behavior. To try and show you what I mean, here is what I see in the output window with and without the yield statement before the line: "programState.setTrainingInstructions()"

With Yield Statement

1a: [-89.52449798583984, -0.0, 0.0]
1b: [0.0, 0.0, 0.0]
2a: [-89.52449798583984, -0.0, 0.0]
2b: [0.0, 0.0, 0.0]

Without Yield Statement

1a: [-89.53038787841797, -0.0, 0.0]
1b: [0.0, 0.0, 0.0]
2a: [0.0, 0.0, 0.0]
2b: [0.0, 0.0, 0.0]

None of this really matters anyway because when I move down the code to call a function that displays the message I want, I must use a yield statement else Vizard gets blocked and the the avatar orientation goes back to [-89.53038787841797, -0.0, 0.0].

I am very sorry for the long-winded post. I had trouble describing the problem I am seeing. Hopefully someone can help me out. Please ask if there is something I have missed out.

Thanks
Reply With Quote
  #2  
Old 12-16-2013, 06:25 AM
chris2307 chris2307 is offline
Member
 
Join Date: Nov 2013
Posts: 36
As promised, the class code for followAvatarTask:

Code:
import viz
import vizshape
import vizact
import vizmat 
import sys
import os

sys.path.append(os.getcwd() + '/functions')#Standard imports above here!

from sensor import Sensor
from messages import Messages
import routeList

class FollowAvatar:
	
	#
	# Training phase constructor. Takes in the current main screen as a parameter which is
	# needed to set up a messages object, in order to display messages to the screen from
	# here. No other set up parameters are taken here because this is called once at the
	# start of the program. setUpExperiment() should be used to initialise a new experiment.
	# Constructor takes care of the default settings which will never change throughtout the
	# various training phases to be completed in the experiment.
	#
	def __init__(self, screenToUse = False):
		
		self.message = Messages(screenToUse)
		
		self.avatar = viz.addAvatar('vcc_male.cfg', pos = [0,0,0])
		self.avatar.state(1)
		self.avatar.disable(viz.DYNAMICS)
		self.avatar.setScale(1,1,1)
		self.avatar.visible(False)
	
		self.shadowTex = viz.addTexture('shadow.png')
		self.shadow = vizshape.addQuad(parent = self.avatar, axis = vizshape.AXIS_Y)
		self.shadow.texture(self.shadowTex)
		self.shadow.zoffset()
	
		self.head = self.avatar.getBone('Bip01 Head')
	
		viz.playSound('footsteps.wav',viz.SOUND_PRELOAD)
		viz.playSound('../sounds/ARETHERE.wav',viz.SOUND_PRELOAD)
		self.avatarSpeak = vizact.speak('../sounds/ARETHERE.wav')

		self.avatar.visible(False)
		
	#
	# Used to setup a new training phase. Takes in the route used for this phase and the failure 
	# coordinates. Failure coordinates are used to detect when a user strays from tha path during 
	# the training phase. The route is used to set up a list of sensors which will be used to detect
	# AND control the avatar movement along the route. Avatar and user position are also initialised here
	#
	def setUpExperiment(self, route, failureCoords):
		
		self.distanceFromAvatar = 0
		self.avatarWalking = True
		self.speakingFlag = True
		self.experimentEnded = False
		self.firstRun = False
		
		self.failureCoordinates = [ Sensor(coord, size=2) for coord in failureCoords ]	
		
		self.avatarStartLocation = [ route[0][0], route[0][1], route[0][2]+5 ]
		self.avatarLookAtLocation = route[1]
		self.goalLocation = route[-1]
		self.startLocation = route[0]
		
		self.sensors = [ Sensor(loc) for loc in route ]
		del self.sensors[0] # We only needed the first coordinate to set the above positions
		
		print "About reset the Avatar Euler"
		self.avatar.setPosition(self.avatarStartLocation)
		self.avatar.lookAt([0,0,0])

		viz.collision(viz.OFF)
		viz.MainView.setPosition(self.startLocation)	
		viz.MainView.reset(viz.HEAD_ORI | viz.BODY_ORI)
		viz.collision(viz.ON)

		self.actions = [
		vizact.method.playsound('footsteps.wav',viz.LOOP),
		vizact.walkTo(self.sensors[0].getPosition(), 1, turnSpeed = 25.0),
		vizact.method.playsound('footsteps.wav',viz.STOP),
		vizact.turn(self.sensors[0].getPosition()),
		]
		self.avatar.visible(True)
		
		return 0

	#
	# The main run function for an experiment instance. Calls the main internal functions used to
	# control the experiment. 
	#
	def runExperiment(self):
		
		if(self.firstRun == False):
			self._runFirst()
			
		self._updatePositionInformation()
		self._checkDistanceFromAvatar()
		self._checkSensors()
		self._checkForStray()
		
	#
	# This function is run once at the start of an experiment. It kick starts the avatar's actions.
	#
	def _runFirst(self):
		self.avatar.runAction(vizact.sequence(self.actions))
		self.firstRun = True
			
	#
	# Updates positional data which is used throughout this class. Also calculated the distance
	# between the user and the avatar.
	#
	def _updatePositionInformation(self):
		self.avatarPos = self.head.getPosition(viz.ABS_GLOBAL)
		self.position = viz.MainView.getPosition()
		self.distanceFromAvatar = vizmat.Distance(self.avatarPos,self.position)
	
	#
	# Checks to see if we are further than 15 metres away from the avatar. If so then the avatar
	# is halted in order to wait for the user to catch up. If not then the avatar continues on
	#
	def _checkDistanceFromAvatar(self):
		if(self.distanceFromAvatar > 15):
			self.avatar.state(1)
			self.avatarWalking = False
			self.avatar.endAction(pool=0)
		elif(self.distanceFromAvatar < 15 and self.avatarWalking == False):
			self.avatar.state(2)
			self.avatarWalking = True
			self.avatar.runAction(vizact.sequence(self.actions))
			
	#
	# Checks the sensors for avatar and player collision. Uses these collisions to determine
	# when the avatar should turn and continue to the next route location as well as sensing
	# when the player has arrived at the final destination of the route which triggers the
	# end of the training phase. 
	#
	def _checkSensors(self):
		if(len(self.sensors) > 1 ):
			if(self.sensors[0].onEnter(self.avatarPos)):				
				try:
					self.nextLocation = self.sensors[1].getPosition()				
				except IndexError:
					print "Index Error"
					
				self.actions = [
				vizact.method.playsound('footsteps.wav',viz.LOOP),
				vizact.walkTo(self.nextLocation, 1, turnSpeed = 200.0),
				vizact.method.playsound('footsteps.wav',viz.STOP),
				vizact.turn(self.goalLocation),
				]
				self.avatar.runAction(vizact.sequence(self.actions))
				del self.sensors[0]
				
		elif(len(self.sensors) == 1 and self.sensors[0].onEnter(self.avatarPos)):
			self.avatar.state(1)
			
			if(self.sensors[0].onEnter(self.position)):
				self.firstRun = False
				self.avatar.visible(False)
				self.experimentEnded = True
			
	def _checkForStray(self):
		pass
		
	def returnAvatarEuler(self):
		return self.avatar.getEuler()
		
	def setAvatarEuler(self):
		self.avatar.lookAt([0,0,0,])
Reply With Quote
  #3  
Old 12-17-2013, 03:58 AM
chris2307 chris2307 is offline
Member
 
Join Date: Nov 2013
Posts: 36
Okay, so I have managed to fix this and perhaps my question could have been asked in a much simpler manner! Forgive me as I was obviously struggling to communicate and operate yesterday (through lack of coffee or whatever!)

So, the problem I was seeing was that I was setting the euler or calling lookAt on the avatar but when I called another function, using yield, the avatar would simply go back to the direction is was facing before.

By running an action and getting the avatar to turn to the direction, I was able to solve this problem. I am guessing that within the main vizard update loop, the avatar actions were still running, and were therefore turning the avatar to the old position. This meant every time I reset the euler and returned control back to Vizard, the updated was "lost". The reason I did not suspect this initially was because when I wanted to "get rid of the avatar" I was only turning the visibility of the avatar to False, setting the state to standing (and it had already reached it's goal location so was no longer travelling). What I wasn't doing was stopping the actions from being run so the turn() function was constantly being called.

Complete oversight on my part but at least now solved. Sorry for not explaining it properly!

Last edited by chris2307; 12-17-2013 at 04:01 AM.
Reply With Quote
Reply

Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
avatar walkto interrupt Meneer_Aart Vizard 6 04-09-2013 03:41 PM
lookat and threads status Philippe Vizard 3 08-12-2010 04:01 AM
Looking through the eyes of an avatar Frank Verberne Vizard 2 04-01-2008 06:52 AM
How to make avatar's eyes to blink when speaking michelcm3 Vizard 12 01-15-2008 09:48 AM


All times are GMT -7. The time now is 04:57 PM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Copyright 2002-2023 WorldViz LLC