Zombie Playground’s Character Pipeline: Designing a Modular Rig Solution

With a greater number of potential characters than Mothhead, our previous Unity project; I went into Zombie Playground’s pre-production with my mind already set on creating an automated rigging solution. I took some inspiration from a Modular Rigging course I followed on 3dBuzz and begun to break down my rigging process into object-oriented code in suite I named Automato.

class BaseSubstruct(object):
    '''
    Abstract class to derive new rig modules from
    '''
    NAME = 'abstractobject'
    NICE_NAME = ''
    CREATION_PARAM_JSON_ATTRNAME = 'creationParams'
    DESCRIPTION = 'generic abstract description'
    MIN_JOINTS = 1  # number of joints needed to create substruct
    REQUIRES_SKELETON_CHAIN = True  # if true, subsruct requires a skeletal chain for creation

    def __init__(self, basename, joints, parent, mainControl):
        '''
        Builds rig in maya and creates an asset node to contain relevant nodes
        '''
        self.container = None
        self.layer = None
        self.containedNodes = list()
        self._rigControls = dict()
        self.lockAttrs = list()
        self.basename = basename  # unique name to identify nodes created by this substruct
        self.parent = parent  # parent to attach top-most rig node
        self.joints = joints  # base skeleton joints to attach
        self.mainControl = mainControl

        self.mainColorAttr = None

        if self.parent is None:
            self.parent = self.joints[0].firstParent2()
            if self.parent:
                self.parent = pm.PyNode(self.parent)
            else:
                if mainControl:
                    self.parent = mainControl

        self.verifyParams()
        self.container = pm.container(name='_'.join([Prefix.CONTAINER, basename, self.NAME]))

        ## Parameter dictionary for storing settings for assets
        ## Children classes can further expand this dictionary with parameters of their own
        self.paramDict = {'classname': self.NAME,
                          'basename': self.basename,
                          'joints': [str(i) for i in self.joints],
                          'parent': str(self.parent),
                          'mainControl': str(self.mainControl),
                          'container': str(self.container)}

        pm.addAttr(self.container, dataType='string', ln=self.CREATION_PARAM_JSON_ATTRNAME)
        pm.addAttr(self.container, at=int, ln='color', min=0, max=31, defaultValue=0, keyable=False, hidden=False)

        self.mainColorAttr = self.container.attr('color')
        self.mainColorAttr.showInChannelBox(True)

        if self.layer is None:
            self.layer = Layers.getLayer(Layers.ANIMATION_CONTROLS)

        self.transform = self.install()
        utils.lockAndHide(self.lockAttrs, True)
        connectControlColors(self.rigControls, self.mainColorAttr)
        self.updateSelectionSets()

        self.layer.addMembers(self._rigControls.values())

        self.containedNodes.append(self.transform)
        self.containedNodes.extend(self._rigControls.values())
        self.container.addNode(self.containedNodes)
        self.containerPublish(self.container)

        self.saveCreationParams()
        pm.parent(self.transform, self.mainControl)

    @property
    def rigControls(self):
        return self._rigControls.values()

    def verifyParams(self):
        if self.REQUIRES_SKELETON_CHAIN and not checkJointsAreHierarchy(self.joints):
            raise SkeletonError('The specified joints must be from the same skeletal chain')

        if len(self.joints) < self.MIN_JOINTS:
            raise SkeletonError('Only works with {0} joints!!'.format(self.MIN_JOINTS))

    def install(self):
        '''
        Main juice function
        Basic rig framework is created
        Return the top-most group node for __init__ method to utilize
        '''
        raise NotImplementedError("Derive me please")

This is a small snippet of the base class I designed all the rig components (known as “Substructs” in my code) to derive from. The BaseSubstruct class containsĀ a set of parameters stored as members that most, or all, of my rigging structures (leg, arm, etc.) share. Using a OOP approach to creating the rig makes for a very scaleable solution for our pipeline. As new creatures are designed and implemented; I can plan out the automation right away, simply making a new class with most of the ground work derived from the original base class. Continue reading

Advertisements

A Look at the Beginnings of Zombie Playground’s Character Pipeline

Zombie Playground is an exciting project I’ve been working on in recent months. Even though this was the second project where we are using Unity3d (the first being Mothhead); this is a completely brand new IP for us, that isn’t tied down to maintaining legacy assets. As such, Zombie Playground offers us a lot of freedom in planning out an efficient pipeline so we can better concentrate on the vision of the game, and less on tedious tasks. I’m responsible for wrangling our said pipeline, documentation, character rigging, and developing tools to assist our small team. We’re still in the early stages of development, and there’s a lot of aspects of the project in WIP-phase, but I want to describe some tools I’ve developed for ZPG that have been coming along nicely.

Continue reading