- -  -  -  - -
         
 

Joystick control
Download! >>
Download c# source code >>



Someone on CGtalk asked how to go about creating a control like this, so I wanted to try to create it in c#. I provided an example scene with the download to demonstrate how you would go about wiring it to objects.

Left-clicking and dragging sets the position value of the target (minimum value of -1, maximum value of 1 for both x and y axis). Right-clicking on the control resets the x and y values to 0. There are also 2 spinners where the user can input a value for a single axis. Right-clicking on these sets that axis to 0.

dotnetObject "maxCustomControl.joystickControl"
PROPERTIES Value Description/notes
bgColor <System.Drawing.Color> Control's back color
gridColor <System.Drawing.Color> Control's grid color
radius <integer> Radius of the target
showGrid <bool> Visibility of the background grid
targetColor <System.Drawing.Color> Control's target color
version (readOnly) <float> The control's version

METHODS Arguments Return value Description/notes
getPosition #(x value, y value) Returns the current position of the control's target
refreshJoystick <void> Forces the control to refresh
setPosition x <float>, y <float> <void> Sets the position of the control's target

EVENTS Args Description/notes
on joystickEvent arg do (...) x <float>, y <float> Triggered whenever the control's value is changed
on spinnerEvent arg do (...)   Triggered when a spinner gets clicked. This is useful for setting enableAccelerators to false (see MAXscript example), otherwise it get override by Max's hotkeys

Max example:
This control works best in a .net form as oppose to Max's dialog.

-- Example on using custom joystick control
(	
	joystickAssembly = ( (getDir #maxRoot) + "joystickControl.dll")-- Point this path to where you placed the .dll
	dotNet.loadAssembly joystickAssembly
	
	rollout dialog_joystick "Joystick control sample"
	(
	-- Some objects already in the scene
		local obj1 = $Point02
		local obj2 = $Point03
		
	-- Add 2 joystick controls to the dialog	
		dotNetControl joystick1 "maxCustomControl.joystickControl" width:150 height:150 align:#left across:2
		dotNetControl joystick2 "maxCustomControl.joystickControl" width:150 height:150 align:#right
		
	-- When the dialog opens, we can initialize some properties	
		on dialog_joystick open do
		(
		-- Set properties	
			dnColor = (dotNetClass "System.Drawing.Color")
			joystick2.bgColor = dnColor.fromArgb 100 200 255
			joystick2.gridColor = dnColor.fromArgb 90 190 220
			joystick2.targetColor = dnColor.orange
			joystick2.radius = 30
			joystick2.refreshJoystick() -- Call this method for the control to properly refresh
			
		-- Look for offset of existing nodes and apply it to the joystick controls
			if (isValidNode obj1) do
			(
				x = obj1.position.controller[2][1].value / 10
				y = obj1.position.controller[2][3].value / 10
				joystick1.setPosition x y
			)
			
			if (isValidNode obj2) do
			(
				x = obj2.position.controller[2][1].value / 10
				y = obj2.position.controller[2][3].value / 10
				joystick2.setPosition x y
			)
		)
		
	-- This event is triggered whenever the value from the joystick control is changed
		on joystick1 joystickEvent arg do
		(
		-- When the position value changes on the joystick, move the max node (this is how we are going to 'wire' the control to the node)	
			if (isValidNode obj1) do
			(
				with animate animButtonState
				(
					obj1.position.controller[2][1].value = (arg.x * 10) -- Multiply by 10 so the node is able to move around more
					obj1.position.controller[2][3].value = (arg.y * 10)
				)
			)
		)
		
		on joystick2 joystickEvent arg do
		(
			if (isValidNode obj2) do
			(
				with animate animButtonState
				(
					obj2.position.controller[2][1].value = (arg.x * 10)
					obj2.position.controller[2][3].value = (arg.y * 10)
				)
			)
		)
		
	-- Need to add this in order to properly get focus when typing in values for spinners
		on joystick1 spinnerEvent arg do enableAccelerators = false
		on joystick2 spinnerEvent arg do enableAccelerators = false
	)
	createDialog dialog_joystick 350 160
)

Maya example:
Before being able to use this control in Maya, you need to enable .net to work with Python. Download this .zip file. It includes 2 files, clr.pyd and Python.Runtime.dll. Place these 2 files in your maya/bin/ directory. In order to make sure it's working, type import clr in a Python tab. If an error doesn't occur then you're good to go! This has been working for me for Maya 2009 64 bit.

For more info to integrate .net in Python, check out http://pythonnet.sourceforge.net/ and http://feihonghsu.blogspot.com/2008/02/installing-pythonnet-20-alpha-2-on.html

# Import all the modules we need
import sys
import clr
import os
import maya.mel as melOps
from System.Windows.Forms import *
from System.Drawing import *

# Import the joystick dll
sys.path.append("C:/Users/Jason/Desktop/") # Point this path to the directory where joystickControl.dll is
clr.AddReference("joystickControl")
import maxCustomControl

class joystickControlForm(Form):
	# Name of objects we are going to effect
	obj1 = "joint1"
	obj2 = "joint2"

	# Check the scene if the objects exist
	obj1Exists = melOps.eval( ("int $TEMPvar = objExists(\"" + obj1 + "\");") )
	obj2Exists = melOps.eval( ("int $TEMPvar = objExists(\"" + obj2 + "\");") )

	def __init__(self):
		# Create the joystick controls
		self.joystickControl_1 = maxCustomControl.joystickControl()
		self.joystickControl_1.Width = 150
		self.joystickControl_1.Height = 150
		
		# Create another joystick control and change how it looks
		self.joystickControl_2 = maxCustomControl.joystickControl()
		self.joystickControl_2.Location = Point(210,0)		
		self.joystickControl_2.bgColor = Color.LightBlue
		self.joystickControl_2.gridColor = Color.Blue
		self.joystickControl_2.targetColor = Color.Yellow
		self.joystickControl_2.radius = 10
		self.joystickControl_2.refreshJoystick()

		# Add event
		self.joystickControl_1.joystickEvent += self.j1ValueChanged
		self.joystickControl_2.joystickEvent += self.j2ValueChanged

		# Create a form
		self.f = Form()
		self.f.Width = 420
		self.f.Height = 240
		self.f.Text = "My form"
		
		# Add the controls to the form
		self.f.Controls.Add(self.joystickControl_1)
		self.f.Controls.Add(self.joystickControl_2)
		self.f.Show()
		
		# Get current offset from the joints and apply it to the joystick controls
		if self.obj1Exists == 1:
			t1 = melOps.eval("getAttr joint1.t;")
			self.joystickControl_1.setPosition(t1[0], t1[1])
		
		if self.obj2Exists == 1:
			t2 = melOps.eval("getAttr joint2.t;")
			self.joystickControl_2.setPosition(t2[0], t2[1])

	# Triggered when the position of the 1st joystick has changed
	def j1ValueChanged(self, sender, args):
		if self.obj1Exists == 1:
			melOps.eval("setAttr " + self.obj1 + ".tx " + str(args.X) + ";") # Set the object's translateX value
			melOps.eval("setAttr " + self.obj1 + ".ty " + str(args.Y) + ";") # Set the object's translateY value
	
	# Triggered when the position of the 2nd joystick has changed
	def j2ValueChanged(self, sender, args):
		if self.obj2Exists == 1:
			melOps.eval("setAttr " + self.obj2 + ".tx " + str(args.X) + ";")
			melOps.eval("setAttr " + self.obj2 + ".ty " + str(args.Y) + ";")

formInst = joystickControlForm()